@FFEK

Делаю SDK. Как объяснить пользователю, иммутабельный геттер или мутабельный?

Прошу прощения за упоротость. Как концепций, реализуемых в моем SDK, так и самого вопроса.

Вот есть
class Cat {
...
}


И есть одна особенность. В этом классе есть метод clone!

То есть можно сделать так
var cat = new Cat();
cat.color = Colors.BLACK;
var cat2 = cat.clone();
cat2.color = Colors.RED;

И получится действительно 2 кота 2 разных цветов.
Круто придумано, да?

Я думаю, что хоть и спорно (тащить подходы из одного языка в другой), но - удобно.

А теперь о проблеме.
Вот есть класс CatPair в нем 2 поля Cat
var catPair = new CatPair();
catPair.male.color = Colors.BLACK;

Как думаете, что произойдет? Ну по канонам JS, здесь действительно должен измениться цвет одного из котов в паре. Пока что все ОК.

Но нужно сделать еще и класс CatValue. В котором тоже будет поле\свойство Cat. И вот оно должно работать иначе.
Задача класса CatValue - это:
1. Дать пользователю возможность заполнить поля формы, для создания кота. CatValueпри этом будет лежать в React state и хранить состояния полей формы. И при onchange полей он будет пытаться сформировать кота, а если это не выходит, то возвращать ошибки, которые также будут класться в стейт, чтобы тут же отобразить их пользователю.
2. После того, как все заполнено верно и делается submit формы, CatValueдолжен вернуть кота, то есть экземпляр класса Cat, и дальнейшая работа будет уже с ним.

В общем, CatValueхранится в стейте и поэтому должен быть иммутабельным.
Это означает, что нельзя сделать так:
const { catValue } = this.state
catValue.cat.colors = Color.BLACK

А только как-то так:
const { catValue } = this.state
const newValue = catValue.changeColor(e.target.value)
setState({ catValue: newValue })

Как это сделать? Поскольку нормальных readonly-полей в JS не завезли, то наверно надо просто:
class CatValue{
...
get cat() {
return this._cat.clone()
}
...
}


ОК, но пользователь-то не знает, что там клонирование. (Это написано в доке, но он не читал.) И нам надо как-то сделать, чтобы пользователь понимал, что это так не работает, что не стоит пытаться это мутировать!

Поэтому думаю, что в данном случае надо сделать не геттер, а метод, причем назвать его так, чтобы попытка мутировать выглядела странно. Например так:
catValue.buildCat().color = Colors.BLACK;
Но есть проблема. То, что это ложь. Метод не билдит кота. А лишь клонирует. ОК, так и назовем:
catValue.cloneCat().color = Colors.BLACK;
Теперь норм. Но не норм с той т\з, что при нормальном получении сбилденного кота непонятно, зачем его клонировать. И как тогда назвать метод?

И норм ли это с учетом того, что все остальное в проекте сделано на основе свойств, а не методов?
  • Вопрос задан
  • 60 просмотров
Пригласить эксперта
Ответы на вопрос 2
sergiks
@sergiks Куратор тега JavaScript
♬♬
Объект кота не в курсе требований иммутабельности стейта. Поэтому эти пляски лучше отложить туда же, где требуется иммутабельность.

Как вариант, использовать immer, чтобы менять глубокие свойства, не беспокоясь об иммутабельности:
import produce from "immer"

const nextState = produce(this.state, draft => {
  // тут можно всё менять, не беспокоясь об иммутабельности
  const { catValue } = draft;
  catValue.cat.colors = Color.BLACK;
});

this.setState(nextState);
или не весь стейт
import produce from "immer"

const { catValue } = this.state;
const nextCatValue = produce(catValue, draft => {
  // тут можно всё менять, не беспокоясь об иммутабельности
  catValue.cat.colors = Color.BLACK;
});

this.setState({ catValue: nextCatValue });
Ответ написан
SilenceOfWinter
@SilenceOfWinter
та еще зажигалка...
Поскольку нормальных readonly-полей в JS не завезли,

https://jsdoc.app/tags-readonly.html

на стороне языка read-only поля получаются с помощью геттеров т.е.
class Cat
{
    constructor(color) {
         this._color = color;
   }
    get color() {
         return this._color;
   }
}

const cat = new Cat('white');
console.log(cat.color)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы