Комментарии 22
Хорошая статья, но как-то слишком много текста что-ли.
И хорошо добавить гифку с игрой, чтобы без перехода было важно, о чем речь
Полностью согласен. Гифки с игрой не хватает, было бы легче визуализировать все модели и логику. Было бы неплохо прогнать статью на опечатки, например "Минималное"
Введите слово
class
, выберите "MAM class definition" и нажмите TAB или ENTER
Если я хоть один раз запускал IDE, я знаю как печатать
Чтобы отформатировать код также как у меня, нажмите CTRL+SHIFT+P, введите "Format" и выберите команду "Format document" :)
У меня Mac+WebStorm и ctrl+shift+p у меня не сработает. У каждого свое окружение, эти описания можно убрать
// Количество пустых трубок
tube_empty_count() { return 2 }
Комментарий просто перевод функции на английский, никакой ценной информации не несет
Блоки кода с пустыми классами только создают больше шума
Тяжело читать, когда столько лишней информации. Настолько подробно описывать действия не стоит. Сосредоточьтесь на коде и осмысленных комментариях.
$mol навсегда останется маргинальной технологией, код-стайл которого заставляет ужасаться любого JS-разработчика, ранее не имевшего дела с таким способом написания кода. А "@ $mol_action" вообще выглядит как способ заглушать ошибки в PHP: https://www.php.net/manual/en/language.operators.errorcontrol.php
Конечно в случае если разработчики фреймворка не перестанут упрямствовать и не перейдут на общепринятый в экосистеме код стайл.
А расскажите пожалста зачем такие хитрости вместо return new $hype_ballsort_tube(this.tube_size())
const obj = new $hype_ballsort_tube
obj.size = () => this.tube_size()
return obj
Это что-то вроде инъекции зависимостей? Но зачем такое усложнение? И напрашивается конечно закешировать геттер, чтобы не создавать функцию на каждый шар
Да, я читал это. Ну так и зачем тут это усложнение? Выглядит очень грязным - поди разберись потом, где меняется size у трубы, если приложение большое.
И все-таки, почему не задавать через конструктор? Если прям нужна связь каналов, то
new $hype_ballsort_tube(() => this.tube_size())
Конкретно в этом случае можно через конструктор передать начальное значение шаров
class $hype_ballsort_tube extends Object {
constructor(public balls_default: Ball[] = []) {}
@ $mol_mem
balls(next?: $hype_ballsort_ball[]) {
return next ?? this.balls_default()
}
}
А код фабрики стал бы таким:
@$mol_mem_key
Tube( index: number ) {
return new $hype_ballsort_tube(this.tube_size())
}
Но в целом, в моле конструкторы используются только дли передачи значений по умолчанию, а инстанцируются объекты из свойств-фабрик $mol_mem_key и $mol_mem(если нужен singleton)
Фабрики используются потому что:
- свойства-фабрики ленивые, т.е. объекты создаются непосредственно при обращении к фабрике. Например это помогает для написания интеграционных тестов для приложения, в одном кейсе поднимется только та часть приложения которая непосредственно участвует в кейсе, а остальная часть не поднимется т.к. все ленивое
- свойства-фабрики управляют жизненым циклом подконтрольных объектов, т.е. если они понимают что они больше не нужны, они прибивают подконтрольные объекты и вызывают у них метод destructor
в котором можно определить что нужно сделать при уничтожении. В целом это может работать и не течь без наличия garbage collector
- и они устанавливают для подконтрольных объектов глобально-уникальные идентификаторы, которые в том числе используются системой реактивности для отслеживания изменений
А конструкторы используются только для передачи значений по умолчаию из нереактивных источников, потому что если бы tube_size
было мемоизированым изменяемым свойством:
@ $mol_mem
tube_size(next?: number) {
return next ?? 4
}
То тогда вариант с получением его значения и передачей в конструктор был бы не желателен, т.к. если бы кто-то изменил значение в tube_size, это вело бы к устареванию фабрики Tube
и повторной актуализацией с пересозданием всех подконтрольных объектов. В некоторых случаях мы бы получили нежелательное поведение.
В таком случае нужно было бы делать как вы написали ниже: `return new $hype_ballsort_tube(() => this.tube_size())`, чтобы небыло вызова tube_size
в фабрике.
Но, я воспользовался общим "паттерном" настройки объекта, который описан в ссылках приведенных ниже.
Спасибо за подробное объяснение, это все очень интересно и круто.
Смущает как раз общий паттерн настройки объекта, вижу в нем нарушение целостности и закрытости объекта, что может приводить к проблемам - добавили новый поток, а переопределить его забыли во всех местах создания. Если внешние зависимости создавать в конструкторе, такой опасности нет - тайпскрипт скажет гав.
А зачем его переопределять во всех местах?
при переопределение тайпскрипт проверяет совместимость сигнатур функций
сама концепция предполагает наличие значений по умолчанию, например если тоже самое описывать на языке view.tree - то тут уже нельзя не указать значение по умолчанию
$hyoo_ballsort_game $mol_object
tube_size 4
Tube* $hype_ballsort_tube
size <= tube_size
это равносильно
class $hyoo_ballsort_game extends $mol_object {
tube_size() { return 4 }
@$mol_mem_key
Tube( index: number ) {
const obj = $hype_ballsort_tube
obj.size = () => this.tube_size()
return obj
}
}
Если не возможно указать значение по умолчанию, то можно сделать так, чтобы разработчик сразу увидел
size() {
throw new Error('Not implemented')
}
Ладно, если Вы не видите тут проблемы, то зачем мне Вам доказывать, что ошибка на этапе компиляции намного дешевле ошибки в рантайме.
Выражение выше не нужно доказывать, я согласен с ним)
Я правильно понял описанный кейс?:
Например есть инпут, который везде в проекте используется. Его отрефакторили, что привело к добавлению нового свойства, которое нужно теперь изменить во всех местах использования этого инпута.
В случае с конструктором тс заругается во всех местах использования. А в моем сценарии нужно будет самому искать места и не будет гарантии, что нигде не забыли?
Примерно да, но у инпута могут быть свои особенности, тут скорее речь про модели. В статье есть модель трубы с шариками, и размер этой трубы задается извне. И нужно ей добавить новое поле, например цвет, тоже задаваемое извне. Если это поле добавить в конструктор, то TS будет ругаться, а если использовать способ предложенный в статье, то будет дефолтный цвет, и нужно будет пройти по всем местам создания этой трубы и добавить руками. А если в параллельной ветке появилось новое место, то очень легко это пропустить в прод.
BALLSORT на $mol. Часть 1