Исходя из опыта ООП (Java), я изучаю Scala самостоятельно. Несмотря на то, что я легко вижу преимущества индивидуального использования неизменяемых объектов, мне трудно понять, как можно создать такое целое приложение. Я приведу пример:
Скажем, у меня есть объекты, которые представляют «материалы» и их свойства (я разрабатываю игру, поэтому у меня действительно есть эта проблема), например, вода и лед. У меня был бы «менеджер», которому принадлежат все такие экземпляры материалов. Одним из свойств будет точка замерзания и плавления, а также то, до чего материал замерзает или плавится.
[РЕДАКТИРОВАТЬ] Все экземпляры материала "singleton", вроде как Enum Java.
Я хочу, чтобы «вода» говорила, что она замерзает до «льда» при 0 ° С, а «лед» говорит, что она плавится до «воды» при 1 ° С. Но если вода и лед являются неизменными, они не могут получить ссылку друг на друга в качестве параметров конструктора, потому что один из них должен быть создан первым, а другой не может получить ссылку на еще не существующий другой в качестве параметра конструктора. Я мог бы решить эту проблему, дав им обоим ссылку на менеджера, чтобы они могли запросить его, чтобы найти другой экземпляр материала, который им нужен каждый раз, когда их спрашивают об их свойствах замораживания / плавления, но затем я получаю ту же проблему между менеджером и материалы, что им нужна ссылка друг на друга, но она может быть предоставлена в конструкторе только для одного из них, поэтому менеджер или материал не могут быть неизменными.
Они просто не могут обойти эту проблему, или мне нужно использовать «функциональные» методы программирования или какой-то другой шаблон для ее решения?
источник
h2o
материалОтветы:
Решение состоит в том, чтобы немного обмануть. В частности:
Создайте A, но оставьте ссылку на B неинициализированной (так как B еще не существует).
Создайте B и укажите на A.
Обновите A, чтобы указать на B. Не обновляйте ни A, ни B после этого.
Это можно сделать явно (пример на C ++):
или неявно (пример в Haskell):
Пример на Haskell использует ленивую оценку для достижения иллюзии взаимозависимых неизменных значений. Значения начинаются как:
a
иb
оба являются действительными головно-нормальными формами независимо. Каждый минус может быть построен без необходимости окончательного значения другой переменной. Когда блок оценивается, он указывает на те же данные, на которыеb
указывает.Таким образом, если вы хотите, чтобы два неизменных значения указывали друг на друга, вы должны либо обновить первое после создания второго, либо использовать механизм более высокого уровня, чтобы сделать то же самое.
В вашем конкретном примере я мог бы выразить это в Haskell как:
Тем не менее, я обошел проблему. Я полагаю, что в объектно-ориентированном подходе, где
setTemperature
метод присоединяется к результату каждогоMaterial
конструктора, вам нужно, чтобы конструкторы указывали друг на друга. Если конструкторы обрабатываются как неизменяемые значения, вы можете использовать подход, описанный выше.источник
В вашем примере вы применяете преобразование к объекту, поэтому я бы использовал что-то вроде
ApplyTransform()
метода, который возвращаетBlockBase
вместо того, чтобы пытаться изменить текущий объект.Например, чтобы заменить IceBlock на WaterBlock, применяя тепло, я бы назвал что-то вроде
и
IceBlock.ApplyTemperature()
метод будет выглядеть примерно так:источник
BlockList.WaterBlock
вместо создания нового блока?BlockList
это простоstatic
класс, который отвечает за отдельные экземпляры каждого блока, поэтому вам не нужно создавать экземплярBlockList
(я привык к C #)Еще один способ разорвать цикл - разделить заботы о материале и трансмутации в некоторых вымышленных языках:
источник
Если вы собираетесь использовать функциональный язык и хотите реализовать преимущества неизменяемости, то вам следует подойти к проблеме с учетом этого. Вы пытаетесь определить тип объекта "лед" или "вода", который может поддерживать диапазон температур - чтобы поддерживать неизменность, вам нужно будет создавать новый объект каждый раз при изменении температуры, что бесполезно. Поэтому постарайтесь сделать понятия типа блока и температуры более независимыми. Я не знаю Scala (это в моем списке для изучения :-)), но, заимствуя у Джои Адамса Ответа в Haskell , я предлагаю что-то вроде:
или, может быть:
(Примечание: я не пытался запустить это, и мой Haskell немного ржавый.) Теперь логика перехода отделена от типа материала, поэтому она не тратит столько памяти, и (на мой взгляд) это довольно немного более функционально ориентированный.
источник