У меня есть несколько больших (более 3 полей) объектов, которые могут и должны быть неизменными. Каждый раз, когда я сталкиваюсь с этим случаем, я склонен создавать мерзкие конструкторы с длинными списками параметров.
Это кажется неправильным, его трудно использовать, а также страдает удобочитаемость.
Еще хуже, если поля представляют собой своего рода коллекцию, например списки. Простой addSibling(S s)
может настолько облегчить создание объекта, но делает объект изменяемым.
Что вы, ребята, используете в таких случаях?
Я использую Scala и Java, но я думаю, что проблема не зависит от языка, если язык является объектно-ориентированным.
Решения, о которых я могу думать:
- «Конструкторские мерзости с длинными списками параметров»
- Паттерн Строитель
java
oop
scala
immutability
Малакс
источник
источник
Ответы:
Что ж, вы хотите, чтобы после создания был как более простой для чтения, так и неизменяемый объект?
Я думаю, что свободный интерфейс ПРАВИЛЬНО СДЕЛАНО вам поможет.
Это будет выглядеть так (чисто выдуманный пример):
Я написал «ПРАВИЛЬНО СДЕЛАНО» жирным шрифтом, потому что большинство Java-программистов неправильно понимают плавные интерфейсы и загрязняют свой объект методом, необходимым для его создания, что, конечно, совершенно неверно.
Хитрость в том, что только метод build () фактически создает Foo (следовательно, Foo может быть неизменным).
FooFactory.create () , где XXX (..) и withXXX (..) создают «что-то еще».
Что-то еще может быть FooFactory, вот один из способов сделать это ...
Ваш FooFactory будет выглядеть так:
источник
Foo
объект, а не имеющие отдельныйFooFactory
.FooImpl
конструктор с 8 параметрами. Какое улучшение?immutable
и боялся, что люди будут повторно использовать фабричные объекты, потому что они так думают. Я имею в виду:FooFactory people = FooFactory.create().withType("person"); Foo women = people.withGender("female").build(); Foo talls = people.tallerThan("180m").build();
гдеtalls
бы сейчас содержались только женщины. Этого не должно происходить с неизменяемым API.В Scala 2.8 вы могли использовать именованные параметры и параметры по умолчанию, а также
copy
метод в классе case. Вот пример кода:источник
Что ж, рассмотрим это на Scala 2.8:
Конечно, здесь есть свои проблемы. Например, попробуйте создать
espouse
иOption[Person]
, а затем поженить двух человек. Я не могу придумать способ решить эту проблему, не прибегая к конструкторуprivate var
и / илиprivate
конструктору плюс фабрика.источник
Вот еще пара вариантов:
Опция 1
Сделайте саму реализацию изменяемой, но разделите интерфейсы, которые она предоставляет, на изменяемые и неизменяемые. Это взято из проекта библиотеки Swing.
Вариант 2
Если ваше приложение содержит большой, но заранее определенный набор неизменяемых объектов (например, объекты конфигурации), вы можете рассмотреть возможность использования инфраструктуры Spring .
источник
Это помогает помнить, что есть разные виды неизменяемости . В вашем случае, я думаю, неизменность "popsicle" будет работать очень хорошо:
Итак, вы инициализируете свой объект, а затем устанавливаете какой-то флаг «замораживания», указывающий, что он больше не доступен для записи. Желательно скрыть мутацию за функцией, чтобы функция оставалась чистой для клиентов, использующих ваш API.
источник
clone()
для получения новых экземпляров.freeze()
метод, но не вызывает его , все может стать некрасивым.Вы также можете сделать так, чтобы неизменяемые объекты отображали методы, которые выглядели как мутаторы (например, addSibling), но позволяли им возвращать новый экземпляр. Это то, что делают неизменяемые коллекции Scala.
Обратной стороной является то, что вы можете создать больше экземпляров, чем необходимо. Это также применимо только тогда, когда существуют промежуточные допустимые конфигурации (например, некоторый узел без братьев и сестер, что нормально в большинстве случаев), если вы не хотите иметь дело с частично построенными объектами.
Например, край графа, у которого еще нет места назначения, не является допустимым ребром графа.
источник
Рассмотрим четыре возможности:
На мой взгляд, каждый из 2, 3 и 4 адаптирован к разной ситуации. Первый из них сложно полюбить по причинам, указанным в OP, и, как правило, он является признаком проекта, который претерпел некоторую нестабильность и требует некоторого рефакторинга.
То, что я перечисляю как (2), хорошо, когда за «фабрикой» нет состояния, тогда как (3) является предпочтительной схемой, когда есть состояние. Я обнаружил, что использую (2), а не (3), когда не хочу беспокоиться о потоках и синхронизации, и мне не нужно беспокоиться об амортизации некоторых дорогостоящих настроек при производстве многих объектов. (3), с другой стороны, вызывается, когда реальная работа идет на создание фабрики (настройка из SPI, чтение файлов конфигурации и т. Д.).
Наконец, в чьем-то другом ответе упоминается вариант (4), где у вас много маленьких неизменяемых объектов, и предпочтительным шаблоном является получение новостей из старых.
Обратите внимание, что я не являюсь членом «фан-клуба выкройки» - конечно, некоторые вещи заслуживают подражания, но мне кажется, что они ведут бесполезную жизнь, когда люди дают им имена и забавные шляпы.
источник
Другой потенциальный вариант - провести рефакторинг, чтобы было меньше настраиваемых полей. Если группы полей работают (в основном) только друг с другом, объедините их в отдельный небольшой неизменяемый объект. Конструкторы / строители этого «маленького» объекта должны быть более управляемыми, как и конструктор / построитель для этого «большого» объекта.
источник
Я использую C #, и это мои подходы. Рассматривать:
Вариант 1. Конструктор с необязательными параметрами.
Используется, например, как
new Foo(5, b: new Bar(whatever))
. Не для версий Java или C # до 4.0. но все же стоит показать, поскольку это пример того, как не все решения зависят от языка.Вариант 2. Конструктор, принимающий объект с одним параметром
Пример использования:
C # начиная с версии 3.0 делает это более элегантным с синтаксисом инициализатора объекта (семантически эквивалентным предыдущему примеру):
Вариант 3.
Измените дизайн вашего класса, чтобы не было необходимости в таком огромном количестве параметров. Вы можете разделить его обязанности на несколько классов. Или передавайте параметры не в конструктор, а только в определенные методы по запросу. Не всегда жизнеспособно, но когда есть, стоит сделать.
источник