У меня были некоторые трудности с дизайном классов. Я читал, что объекты раскрывают их поведение, а не данные; следовательно, вместо использования методов получения / установки для изменения данных, методы данного класса должны быть «глаголами» или действиями, действующими на объект. Например, в объекте «Счета», мы имели бы методы Withdraw()
и Deposit()
вместо того , и setAmount()
т.д. См: Почему методы получения и установки являются злом .
Так, например, учитывая класс Customer, который хранит много информации о клиенте, например, имя, DOB, телефон, адрес и т. Д., Как можно избежать получения / установки всех этих атрибутов? Какой метод типа «Поведение» можно написать, чтобы заполнить все эти данные?
java
object-oriented
IntelliData
источник
источник
name()
наCustomer
это ясно, или яснее, чем метод , называемыйgetName()
.Ответы:
Как указывалось в довольно многих ответах и комментариях, DTO являются подходящими и полезными в некоторых ситуациях, особенно при передаче данных через границы (например, сериализация в JSON для отправки через веб-сервис). В оставшейся части этого ответа я более или менее проигнорирую это и поговорю о классах домена и о том, как они могут быть спроектированы так, чтобы минимизировать (если не исключать) методы получения и установки, и все же быть полезными в большом проекте. Я также не буду говорить о том, зачем удалять геттеры или сеттеры или когда это делать, потому что это их собственные вопросы.
В качестве примера представьте, что ваш проект представляет собой настольную игру, такую как Chess или Battleship. У вас могут быть различные способы представления этого на уровне представления (консольное приложение, веб-служба, графический интерфейс и т. Д.), Но у вас также есть основной домен. Один класс, который вы можете иметь
Coordinate
, представляет позицию на доске. «Злой» способ написать это будет:(Я собираюсь писать примеры кода на C #, а не на Java, для краткости и потому, что я более знаком с этим. Надеюсь, это не проблема. Концепции одинаковы, и перевод должен быть простым.)
Удаление сеттеров: неизменность
В то время как публичные геттеры и сеттеры являются потенциально проблематичными, сеттеры являются гораздо более «злым» из двух. Их также обычно легче устранить. Процесс прост - установить значение из конструктора. Любые методы, которые ранее мутировали объект, должны вместо этого возвращать новый результат. Так:
Обратите внимание, что это не защищает от других методов в классе, изменяющих X и Y. Чтобы быть более неизменными, вы можете использовать
readonly
(final
в Java). Но так или иначе - независимо от того, делаете ли вы свои свойства по-настоящему неизменными или просто предотвращаете прямую публичную мутацию через сеттеры - это помогает избавиться от ваших публичных сеттеров. В подавляющем большинстве ситуаций это работает просто отлично.Извлечение добытчиков, часть 1: проектирование поведения
Все вышеперечисленное хорошо для сеттеров, но с точки зрения добытчиков, мы даже застрелились в ногу еще до старта. Наш процесс состоял в том, чтобы подумать, что такое координата - данные, которые она представляет, - и создать класс вокруг этого. Вместо этого мы должны были начать с того, какое поведение нам нужно из координаты. Кстати, этому процессу помогает TDD, где мы извлекаем такие классы только тогда, когда они нам нужны, поэтому мы начинаем с желаемого поведения и работаем оттуда.
Итак, скажем, первое место, в котором вы нуждались,
Coordinate
было обнаружение столкновений: вы хотели проверить, занимают ли две фигуры одинаковое пространство на доске. Вот «злой» путь (конструкторы для краткости опущены):И вот хороший способ:
(
IEquatable
реализация сокращенно для простоты). Разрабатывая поведение, а не моделируя данные, нам удалось удалить наших получателей.Обратите внимание, что это также относится к вашему примеру. Вы можете использовать ORM или отображать информацию о клиенте на веб-сайте или что-то в этом случае, и в этом случае какой-то
Customer
DTO, вероятно, будет иметь смысл. Но только то, что ваша система включает в себя клиентов и они представлены в модели данных, не означает автоматически, что у вас должен бытьCustomer
класс в вашем домене. Возможно, когда вы разрабатываете поведение, оно появится, но если вы хотите избежать получателей, не создавайте их превентивно.Удаление добытчиков, часть 2: внешнее поведение
Таким образом, выше , это хорошее начало, но рано или поздно вы, вероятно , столкнуться с ситуацией , когда у вас есть поведение , которое связано с классом, который каким - то образом зависит от состояния класса, но не принадлежит по классу. Такое поведение обычно происходит на уровне обслуживания вашего приложения.
Взяв наш
Coordinate
пример, в конечном итоге вы захотите представить свою игру пользователю, а это может означать рисование на экране. Например, у вас может быть проект пользовательского интерфейса, который используетсяVector2
для представления точки на экране. Но было бы неуместно, чтобыCoordinate
класс взял на себя ответственность за преобразование координаты в точку на экране, что привело бы к всевозможным проблемам с представлением в вашей основной области. К сожалению, этот тип ситуации присущ ОО-дизайну.Первый вариант , который очень часто выбирают, это просто разоблачить проклятых добытчиков и сказать им, черт побери. Это имеет преимущество простоты. Но так как мы говорим о том, чтобы избегать получателей, скажем, ради аргумента, мы отвергаем этот и посмотрим, какие есть другие варианты.
Второй вариант - добавить какой-нибудь
.ToDTO()
метод в ваш класс. Это - или подобное - вполне может понадобиться в любом случае, например, когда вы хотите сохранить игру, вам нужно захватить почти все ваше состояние. Но разница между выполнением этого для ваших услуг и простым доступом к получателю напрямую более или менее эстетична. В нем все еще столько же «зла».Третий вариант, который, как я видел, защищал Зоран Хорват в паре видео Pluralsight, - это использование модифицированной версии шаблона посетителя. Это довольно необычное использование и вариация схемы, и я думаю, что пробег людей сильно зависит от того, добавляет ли он сложность без реальной выгоды, или это хороший компромисс для ситуации. По сути, идея состоит в том, чтобы использовать стандартный шаблон посетителя, но чтобы
Visit
методы принимали необходимое состояние в качестве параметров вместо класса, который они посещают. Примеры можно найти здесь .Для нашей проблемы, решение с использованием этого шаблона будет:
Как вы можете сказать,
_x
и_y
не являются действительно инкапсулированные больше. Мы могли бы извлечь их, создавая,IPositionTransformer<Tuple<int,int>>
который просто возвращает их напрямую. В зависимости от вкуса, вы можете чувствовать, что это делает все упражнение бессмысленным.Тем не менее, с публичными получателями очень легко сделать что-то не так, просто извлекая данные напрямую и используя их в нарушение « Скажите, не спрашивайте» . В то время как с помощью этого шаблона на самом деле проще сделать его правильно: когда вы хотите создать поведение, вы автоматически начнете с создания типа, связанного с ним. Нарушения TDA будут очень вонючими и, вероятно, потребуют решения более простого, лучшего решения. На практике эти пункты значительно облегчают правильное выполнение, ОО, способом, чем «злой», который поощряют добывающие.
Наконец , даже если это изначально неочевидно, на самом деле могут быть способы раскрыть достаточно того, что вам нужно, как поведение, чтобы избежать необходимости выставлять состояние. Например, используя нашу предыдущую версию
Coordinate
, единственным открытым членом которой являетсяEquals()
(на практике это потребует полнойIEquatable
реализации), вы можете написать следующий класс на уровне представления:Оказывается, что удивительно, оказывается, что все поведение, которое нам действительно требовалось от координаты для достижения нашей цели, было проверкой на равенство! Конечно, это решение адаптировано к этой проблеме и делает предположения о приемлемом использовании / производительности памяти. Это всего лишь пример, который подходит для этой конкретной предметной области, а не план для общего решения.
И снова, мнения будут различаться относительно того, является ли на практике это ненужной сложностью. В некоторых случаях такого решения не существует, или оно может быть непонятно странным или сложным, и в этом случае вы можете вернуться к вышеуказанным трем.
источник
Customer
класс, требующий изменения своего телефонного номера? Возможно, телефонный номер клиента меняется, и мне нужно сохранить это изменение в базе данных, но ни одно из этих действий не является обязанностью объекта домена, обеспечивающего поведение. Это проблема доступа к данным, и, вероятно, она будет решена с помощью DTO и, скажем, хранилища.Customer
данных объекта домена относительно свежим (синхронно с базой данных) - это вопрос управления его жизненным циклом, который также не является его собственной ответственностью, и, вероятно, он снова окажется в хранилище, на фабрике или в контейнере IOC или что бы ни создавалоCustomer
с.Самый простой способ избежать установки - это передать значения в метод конструктора, когда вы
new
поднимаете объект. Это также обычный шаблон, когда вы хотите сделать объект неизменным. Тем не менее, вещи не всегда так ясны в реальном мире.Это правда, что методы должны быть о поведении. Однако некоторые объекты, такие как Customer, существуют в первую очередь для хранения информации. Это те виды объектов, которые больше всего выигрывают от геттеров и сеттеров; если бы в таких методах не было нужды, мы бы просто полностью их ликвидировали.
Дальнейшее чтение
Когда оправданы геттеры и сеттеры
источник
setEvil(null);
Прекрасно иметь объект, который предоставляет данные, а не поведение. Мы просто называем это «объект данных». Шаблон существует под такими именами, как Data Transfer Object или Value Object. Если целью объекта является хранение данных, то методы получения и установки действительны для доступа к данным.
Так почему кто-то сказал бы, что методы получения и установки - это зло? Вы увидите это очень часто - кто-то возьмет руководство, которое совершенно правильно в определенном контексте, а затем удалит контекст, чтобы получить более жесткий заголовок. Например, « композиция благосклонности перед наследованием » является хорошим принципом, но довольно скоро кто-то собирается удалить контекст и написать « Почему расширяется зло » (эй, тот же автор, какое совпадение!) Или « наследование зло и должно быть уничтожен ".
Если вы посмотрите на содержание статьи, то на самом деле у нее есть несколько правильных точек, она просто растягивает точку, чтобы создать заголовок с нажатием кнопки мыши. Например, в статье говорится, что детали реализации не должны раскрываться. Это принципы инкапсуляции и сокрытия данных, которые являются фундаментальными в ОО. Однако метод получения по определению не раскрывает детали реализации. В случае объекта данных « Клиент» свойства « Имя» , « Адрес» и т. Д. Не являются деталями реализации, а представляют собой цели объекта и должны быть частью общедоступного интерфейса.
Прочитайте продолжение статьи, на которую вы ссылаетесь, чтобы увидеть, как он предлагает на самом деле устанавливать свойства, такие как «имя» и «зарплата», для объекта «Сотрудник» без использования злых сеттеров. Оказывается, он использует шаблон с 'Exporter', который заполняется методами, называемыми add Name, add Salary, который, в свою очередь, устанавливает поля с одинаковыми именами ... Так что, в конце концов, он заканчивает тем, что использует именно шаблон сеттера, просто с другое соглашение об именах.
Это все равно что думать, что вы избегаете ловушек синглетонов, переименовывая их в единственные вещи, сохраняя при этом ту же реализацию.
источник
Чтобы преобразовать
Customer
-класс из объекта данных, мы можем задать себе следующие вопросы о полях данных:Как мы хотим использовать {поле данных}? Где используется {поле данных}? Можно и нужно ли использовать {поле данных} в классе?
Например:
Какова цель
Customer.Name
?Возможные ответы, отображение имени на странице входа в систему, использование имени в рассылках клиенту.
Что приводит к методам:
Какова цель
Customer.DOB
?Проверка возраста клиента. Скидки на день рождения клиента. Mailings.
С учетом комментариев пример объекта
Customer
- как объекта данных, так и «реального» объекта со своими обязанностями - слишком широк; то есть у него слишком много свойств / обязанностей. Что приводит либо к большому количеству компонентов в зависимости отCustomer
(путем чтения его свойств), либо кCustomer
зависимости от множества компонентов. Возможно, существуют разные взгляды клиента, возможно, у каждого должен быть свой отдельный класс 1 :Клиент в контексте
Account
и денежных транзакций, вероятно, используется только для:Account
с.Этот клиент не нуждается в области , как
DOB
,FavouriteColour
,Tel
, и , возможно , даже неAddress
.Клиент в контексте входа пользователя на банковский сайт.
Соответствующие поля:
FavouriteColour
который может прийти в форме персонализированной темы;LanguagePreferences
, а такжеGreetingName
Вместо свойств с геттерами и сеттерами они могут быть записаны одним методом:
Заказчик в контексте маркетинга и персонализированной рассылки.
Здесь не полагаться на свойства объекта данных, а вместо этого, начиная с ответственности объекта; например:
Тот факт, что этот объект клиента имеет
FavouriteColour
свойство и / илиAddress
свойство, становится неактуальным: возможно, реализация использует эти свойства; но он также может использовать некоторые методы машинного обучения и использовать предыдущие взаимодействия с клиентом, чтобы выяснить, какие продукты могут заинтересовать клиента.1. Разумеется,
Customer
иAccount
классы были примеры, и для простого примера или домашнее задание упражнения, расщепляющий этот клиент может быть излишним, но на примере расщепления, я надеюсь показать , что метод превращения объекта данных в объект с обязанности будут работать.источник
Customer.FavoriteColor
?TL; DR
Моделирование поведения - это хорошо.
Моделирование для хороших (!) Абстракций лучше.
Иногда требуются объекты данных.
Поведение и Абстракция
Есть несколько причин, чтобы избежать геттеров и сеттеров. Один из них, как вы заметили, состоит в том, чтобы избежать моделирования данных. Это на самом деле второстепенная причина. Основная причина заключается в предоставлении абстракции.
В вашем примере с банковским счетом ясно:
setBalance()
метод будет очень плохим, потому что установка баланса - это не то, для чего должен использоваться счет. Поведение счета должно максимально абстрагироваться от его текущего баланса. Он может принимать во внимание баланс при принятии решения о сбое вывода средств, он может предоставлять доступ к текущему балансу, но изменение взаимодействия с банковским счетом не должно требовать от пользователя расчета нового баланса. Это то, что аккаунт должен делать сам.Даже пара
deposit()
иwithdraw()
методы не идеальны для моделирования банковского счета. Лучшим способом было бы предоставить только одинtransfer()
метод, который принимает другой учет и количество в качестве аргументов. Это позволило бы классу учетной записи тривиально гарантировать, что вы случайно не создадите / уничтожите деньги в своей системе, это обеспечит очень удобную абстракцию и фактически предоставит пользователям больше понимания, потому что это заставит использовать специальные учетные записи для заработанные / вложенные / потерянные деньги (см. двойной учет ). Конечно, не каждому использованию учетной записи необходим такой уровень абстракции, но определенно стоит подумать, сколько абстракции могут обеспечить ваши классы.Обратите внимание, что предоставление абстракции и скрытие внутренних данных не всегда одно и то же. Практически любое приложение содержит классы, которые фактически являются данными. Кортежи, словари и массивы являются частыми примерами. Вы не хотите скрывать x-координату точки от пользователя. Существует очень мало абстракций, которые вы можете / должны делать с точкой.
Класс клиента
Клиент - это, безусловно, объект в вашей системе, который должен попытаться предоставить полезные абстракции. Например, он, скорее всего, должен быть связан с корзиной покупок, а комбинация корзины и покупателя должна позволять совершать покупки, что может привести к таким действиям, как отправка ему запрошенных товаров, снятие с него денег (с учетом его выбранного платежа). метод) и др.
Суть в том, что все данные, которые вы упомянули, связаны не только с клиентом, все эти данные также являются изменчивыми. Клиент может переехать. Они могут поменять свою компанию кредитной карты. Они могут изменить свой адрес электронной почты и номер телефона. Черт, они могут даже изменить свое имя и / или пол! Таким образом, полнофункциональный клиентский класс действительно должен обеспечивать полный модифицирующий доступ ко всем этим элементам данных.
Тем не менее, установщики могут / должны предоставлять нетривиальные услуги: они могут обеспечить правильный формат адресов электронной почты, проверку почтовых адресов и т. Д. Аналогично, «получатели» могут предоставлять услуги высокого уровня, такие как предоставление адресов электронной почты в
Name <user@server.com>
формате использование полей имени и депонированного адреса электронной почты или предоставление правильно отформатированного почтового адреса и т. д. Конечно, то, что из этой функциональности высокого уровня имеет смысл, сильно зависит от вашего варианта использования. Это может быть полным излишним, или может потребоваться, чтобы другой класс сделал это правильно. Выбор уровня абстракции не из легких.источник
Пытаясь расширить ответ Каспера, проще всего ругать и уничтожать сеттеров. В довольно смутном, махающем рукой (и, надеюсь, юмористическом) аргументе:
Когда изменится Customer.Name?
Редко. Может быть, они поженились. Или пошел в защиту свидетелей. Но в этом случае вы также захотите проверить и, возможно, изменить их место жительства, ближайших родственников и другую информацию.
Когда изменится DOB?
Только при первоначальном создании или при вводе данных. Или если они бейсболист Domincan. :-)
Эти поля не должны быть доступны обычным, обычным установщикам. Возможно, у вас есть
Customer.initialEntry()
метод илиCustomer.screwedUpHaveToChange()
метод, требующий специальных разрешений. Но нет публичногоCustomer.setDOB()
метода.Обычно Заказчик читается из базы данных, API REST, некоторого XML, что угодно. Иметь метод
Customer.readFromDB()
или, если вы более строго относитесь к SRP / разделению интересов, у вас будет отдельный компоновщик, напримерCustomerPersister
объект сread()
методом. Внутренне они как-то устанавливают поля (я предпочитаю использовать доступ к пакету или внутренний класс, YMMV). Но опять же, избегайте публичных сеттеров.(Приложение как Вопрос несколько изменилось ...)
Допустим, ваше приложение интенсивно использует реляционные базы данных. Было бы глупо иметь
Customer.saveToMYSQL()
илиCustomer.readFromMYSQL()
методы. Это создает нежелательную связь с конкретной, нестандартной и, вероятно, изменяющейся сущностью. Например, когда вы меняете схему или меняете ее на Postgress или Oracle.Тем не менее, ИМО, это вполне приемлемо для пары Клиента к абстрактному стандарта ,
ResultSet
. Отдельный вспомогательный объект (я назову егоCustomerDBHelper
, который, вероятно, является подклассомAbstractMySQLHelper
) знает обо всех сложных соединениях с вашей БД, знает хитрые детали оптимизации, знает таблицы, запросы, объединения и т. Д. (Или использует ORM как Hibernate) для генерации ResultSet. Ваш объект говорит кResultSet
, который является абстрактным стандартом , вряд ли изменится. Когда вы изменяете базовую базу данных или меняете схему, Customer не меняется , а CustomerDBHelper . Если вам повезет, то изменяется только AbstractMySQLHelper, который автоматически вносит изменения для клиента, продавца, доставки и т. Д.Таким образом, вы можете (возможно) избежать или уменьшить потребность в геттерах и сеттерах.
И, главное в статье о Holub, сравните и сопоставьте вышесказанное с тем, что было бы, если бы вы использовали геттеры и сеттеры для всего и изменили базу данных.
Точно так же, скажем, вы используете много XML. ИМО, хорошо связать вашего Заказчика с абстрактным стандартом, таким как Python xml.etree.ElementTree или Java org.w3c.dom.Element . Клиент получает и устанавливает себя от этого. Опять же, вы можете (возможно) уменьшить потребность в геттерах и сеттерах.
источник
Проблема наличия методов получения и установки может быть связана с тем, что класс может использоваться в бизнес-логике одним способом, но у вас также могут быть вспомогательные классы для сериализации / десериализации данных из базы данных или файла или другого постоянного хранилища.
В связи с тем, что существует много способов хранения / извлечения ваших данных, и вы хотите отделить объекты данных от способа их хранения, инкапсуляция может быть «скомпрометирована», либо сделав эти элементы общедоступными, либо сделав их доступными через получатели и сеттеры, что почти так же плохо, как сделать их публичными.
Есть разные способы обойти это. Один из способов - сделать данные доступными «другу». Хотя дружба не наследуется, это может быть преодолено любым сериализатором, запрашивающим информацию у друга, т.е. базовым сериализатором, «пересылающим» информацию.
Ваш класс может иметь универсальный метод "fromMetadata" или "toMetadata". Метаданные from конструируют объект, поэтому вполне могут быть конструктором. Если это язык с динамической типизацией, метаданные довольно стандартны для такого языка и, вероятно, являются основным способом создания таких объектов.
Если ваш язык специфичен для C ++, одним из способов является использование общедоступной «структуры» данных, а затем, чтобы ваш класс имел экземпляр этой «структуры» в качестве члена и фактически все данные, которые вы собираетесь хранить / восстановить, чтобы быть сохраненным в нем. Затем вы можете легко написать «обертки» для чтения / записи ваших данных в нескольких форматах.
Если ваш язык - C # или Java, у которых нет «структур», то вы можете сделать то же самое, но ваша структура теперь является вторичным классом. Реального понятия «владения» данными или константностью не существует, поэтому, если вы выдадите экземпляр класса, содержащий ваши данные, и он будет общедоступным, все, что попадется, может его изменить. Вы можете «клонировать» его, хотя это может быть дорого. В качестве альтернативы вы можете сделать этот класс иметь личные данные, но использовать методы доступа. Это дает пользователям вашего класса окольный способ получить доступ к данным, но это не прямой интерфейс с вашим классом, а детальная информация о хранении данных класса, что также является вариантом использования.
источник
ООП - это инкапсуляция и скрытие поведения внутри объектов. Объекты - это черные ящики. Это способ дизайна вещи. Во многих случаях активом не нужно знать внутреннее состояние другого компонента, и лучше не знать его. Вы можете реализовать эту идею главным образом с помощью интерфейсов или внутри объекта с видимостью и следя за тем, чтобы для вызывающей стороны были доступны только разрешенные глаголы / действия.
Это хорошо работает для какой-то проблемы. Например, в пользовательских интерфейсах для моделирования отдельных компонентов пользовательского интерфейса. Когда вы взаимодействуете с текстовым полем, вас интересует только установка текста, его получение или прослушивание события изменения текста. Как правило, вас не интересует, где находится курсор, шрифт, используемый для рисования текста, или как используется клавиатура. Инкапсуляция обеспечивает много здесь.
Напротив, когда вы вызываете сетевую службу, вы предоставляете явный ввод. Там обычно грамматика (как в JSON или XML) и все возможности вызова службы не имеют причин быть скрытыми. Идея заключается в том, что вы можете называть службу так, как вам нужно, а формат данных является общедоступным и опубликованным.
В этом случае или многих других (например, доступ к базе данных) вы действительно работаете с общими данными. Таким образом, нет причин скрывать это, напротив, вы хотите сделать его доступным. Может возникнуть проблема с доступом для чтения / записи или согласованностью проверки данных, но на этом ядре это основная концепция, если она общедоступна.
Для такого требования к дизайну, когда вы хотите избежать инкапсуляции и обнародовать информацию, вы должны избегать объектов. Что вам действительно нужно, так это кортежи, структуры C или их эквиваленты, а не объекты.
Но это также происходит в таких языках, как Java, единственное, что вы можете моделировать, это объекты или массивы объектов. Объекты сами по себе могут содержать несколько нативных типов (int, float ...), но это все. Но объекты также могут вести себя как простая структура с простыми открытыми полями и всем этим.
Поэтому, если вы моделируете данные, вы можете сделать это только с помощью открытых полей внутри объектов, потому что вам не нужно больше. Вы не используете инкапсуляцию, потому что она вам не нужна. Это делается на многих языках. Исторически сложилось так, что в Java стандартная роза, где с помощью getter / setter вы могли бы по крайней мере иметь контроль чтения / записи (не добавляя, например, setter), и что инструментарий и инфраструктура, использующие API для инспектирования, будут искать методы getter / setter и использовать их автозаполнение содержимого или отображение тезисов в виде изменяемых полей в автоматически сгенерированном пользовательском интерфейсе.
Там также аргумент, который вы можете добавить немного логики / проверки в методе setter.
В действительности нет почти никакого оправдания для методов получения / установки, поскольку они чаще всего используются для моделирования чистых данных. Фреймворки и разработчики, использующие ваши объекты, ожидают, что получатель / установщик в любом случае сделает только установку / получение полей. Вы фактически делаете с getter / setter не больше, чем то, что можно сделать с открытыми полями.
Но это старые привычки, а старые привычки трудно устранить ... Ваши коллеги или учителя могут даже угрожать вам, если вы не будете вслепую ставить методологи / сеттеры вслепую, если у них нет опыта, чтобы лучше понять, что они и чем они являются. не.
Скорее всего, вам придется изменить язык, чтобы использовать все эти шаблоны. (Как C # или lisp). Для меня геттеры / сеттеры - просто еще одна ошибка в один миллиард долларов ...
источник
@Getter @Setter class MutablePoint3D {private int x, y, z;}
.Я думаю, что этот вопрос колючий, потому что вы беспокоитесь о методах поведения для заполнения данных, но я не вижу никаких признаков того, какое поведение
Customer
класс объектов предназначен для инкапсуляции.Не путайте
Customer
как класс объектов с «Заказчиком» как пользователем / субъектом, который выполняет различные задачи с использованием вашего программного обеспечения.Когда вы говорите, что у вас есть класс Customer, который хранит много информации о клиенте, то по поведению кажется, что ваш класс Customer мало чем отличает его от скалы. A
Rock
может иметь цвет, вы можете дать ему имя, у вас может быть поле для хранения его текущего адреса, но мы не ожидаем какого-либо интеллектуального поведения от камня.Из связанной статьи о том, что геттеры / сеттеры являются злыми:
Без какого-либо определенного поведения ссылка на камень как на элемент
Customer
не меняет того факта, что это просто объект с некоторыми свойствами, которые вы хотели бы отслеживать, и не имеет значения, какие приемы вы хотите использовать, чтобы уйти от добытчиков и сеттера. Скале не важно, имеет ли оно правильное имя, и скала не будет знать, действителен ли адрес или нет.Ваша система заказов может связать a
Rock
с заказом на покупку, и, если дляRock
него определен адрес, некоторая часть системы может обеспечить доставку элемента в камень.Во всех этих случаях
Rock
это просто объект данных, и он будет оставаться единым, пока мы не определим конкретные варианты поведения с полезными результатами вместо гипотетических.Попробуй это:
Когда вы избегаете перегрузки слова «Клиент» двумя потенциально разными значениями, это должно облегчить понимание.
Размещает ли
Rock
объект Орден или это то, что делает человек, нажимая на элементы пользовательского интерфейса, чтобы инициировать действия в вашей системе?источник
Я добавляю свои 2 цента здесь, упоминая подход, говорящий на SQL .
Этот подход основан на понятии автономного объекта. У него есть все ресурсы, необходимые для реализации его поведения. Не нужно рассказывать, как выполнять свою работу - достаточно декларативного запроса. И объект определенно не должен содержать все свои данные как свойства класса. Это действительно не имеет и не должно иметь значения, откуда они берутся.
Говоря о совокупности , неизменность также не является проблемой. Скажем, у вас есть последовательность состояний, которые может содержать агрегат: вполне нормально реализовать каждое состояние как отдельный объект. Возможно, вы могли бы пойти еще дальше: поговорить с экспертом вашего домена. Скорее всего, он или она не видит эту совокупность как некое единое целое. Вероятно, у каждого государства есть свое значение, заслуживающее своего собственного объекта.
Наконец, я хотел бы отметить, что процесс поиска объектов очень похож на разложение системы на подсистемы . Оба основаны на поведении, а не на чем-то еще.
источник