Подтвердить что ты не робот

Разве не эксперт по информации/не скажите, не задавайте вопросы с принципом единой ответственности?

Это, наверное, только я, поэтому я задаю вопрос. Информационный эксперт, "Скажите не спрашивайте", и SRP часто упоминаются вместе как лучшие практики. Но я думаю, что они не согласны. Вот о чем я говорю:

Код, который поддерживает SRP, но нарушает Tell Do not Ask, Info Expert:

Customer bob = ...;
// TransferObjectFactory has to use Customer accessors to do its work, 
// violates Tell Don't Ask
CustomerDTO dto = TransferObjectFactory.createFrom(bob); 

Код, который рекомендует Tell Do not Ask/Info Expert, но нарушает SRP:

Customer bob = ...;
// Now Customer is doing more than just representing the domain concept of Customer,
// violates SRP
CustomerDTO dto = bob.toDTO();

Если они действительно расходятся, это оправдание моего ОКР. В противном случае, пожалуйста, наполните меня тем, как эти практики могут сосуществовать мирно. Спасибо.

Изменить: кто-то хочет определить термины -

Эксперт по информации: объекты, которые имеют данные, необходимые для операции, должны содержать операцию

Скажите не спрашивайте: не просите объекты для данных для выполнения работы; расскажите объектам о работе

Единый принцип ответственности: каждый объект должен иметь узко определенную ответственность

4b9b3361

Ответ 1

Я не думаю, что они настолько расходятся, что подчеркивают разные вещи, которые причинят вам боль. Один из них касается структурирования кода, чтобы дать понять, где конкретные обязанности и уменьшить связь, а другое - уменьшить причины для изменения класса.

Мы все должны принимать решения каждый день о том, как структурировать код и какие зависимости мы хотим внедрить в проекты.

Мы создали множество полезных рекомендаций, максим и шаблонов, которые могут помочь нам принять решения.

Каждый из них полезен для обнаружения различных проблем, которые могут присутствовать в наших проектах. Для любой конкретной проблемы, которую вы можете посмотреть, где-то будет сладкое место.

Различные рекомендации противоречат друг другу. Просто применяя каждое руководство, которое вы слышали или прочитали, не улучшит ваш дизайн.

По конкретной проблеме, которую вы смотрите сегодня, вам нужно решить, какие наиболее важные факторы, которые могут причинить вам боль, - это.

Ответ 2

Вы можете говорить о "Tell Do not Ask", когда вы запрашиваете состояние объекта, чтобы сообщить объекту что-то сделать.

В вашем первом примере TransferObjectFactory.createFrom просто конвертер. Он не сообщает объекту Customer что-то делать после проверки состояния.

Я думаю, что первый пример правильный.

Ответ 3

Эти классы не расходятся. DTO просто служит каналом данных из хранилища, предназначенным для использования в качестве немого контейнера. Это, безусловно, не нарушает СРП.

С другой стороны, метод .toDTO вызывает сомнение - почему клиент должен взять на себя эту ответственность? Для "чистоты" у меня был бы другой класс, который должен был создать DTO из бизнес-объектов, таких как Customer.

Не забывайте, что эти принципы являются принципами, и когда вы можете использовать простейшие решения до тех пор, пока не измените требования, создайте проблему, сделайте это. Безупречную сложность, безусловно, нужно избегать.

Я очень рекомендую, BTW, Robert C. Martin Agile Patterns, Practices и принципы для более глубокого изучения этого предмета.

Ответ 4

DTO с сестринским классом (как и у вас) нарушают все три принципа, которые вы заявили, и инкапсуляцию, поэтому у вас проблемы.

Для чего вы используете этот CustomerDTO, и почему вы не можете просто использовать Customer и иметь данные DTO внутри клиента? Если вы не будете осторожны, CustomerDTO будет нуждаться в Клиенте, а Клиенту потребуется CustomerDTO.

TellDontAsk говорит, что если вы принимаете решение о состоянии одного объекта (например, клиента), то это решение должно выполняться внутри самого класса клиента.

Например, если вы хотите напомнить Клиенту о выплате любых невыплаченных векселей, вы вызываете

  List<Bill> bills = Customer.GetOutstandingBills();
  PaymentReminder.RemindCustomer(customer, bills);

Это нарушение. Вместо этого вы хотите сделать

Customer.RemindAboutOutstandingBills() 

(и, конечно же, вам нужно будет передать в PaymentReminder зависимость от конструирования клиента).

Информационный эксперт говорит то же самое в значительной степени.

Принцип единой ответственности может быть легко понят - он говорит, что у класса клиента должна быть одна ответственность, но также за то, что ответственность за группирование данных, методов и других классов, согласованных с концепцией "Заказчик", должна быть инкапсулирована только одним классом, То, что составляет единую ответственность, крайне сложно точно определить, и я бы рекомендовал больше читать по этому вопросу.

Ответ 5

Крейг Ларман обсудил это, когда он представил GRASP в применении UML и шаблонов к объектно-ориентированному анализу и дизайну и итеративной разработке (2004):

В некоторых ситуациях решение, предлагаемое экспертом, нежелательно, как правило, из-за проблем в сцеплении и сцеплении (эти принципы обсуждаются далее в этой главе).

Например, кто должен отвечать за сохранение продажи в базе данных? Конечно, большая часть информации, которую нужно сохранить, находится в объекте Sale, и, таким образом, эксперт может утверждать, что ответственность лежит в классе Sale. И, путем логического расширения этого решения, каждый класс будет иметь свои собственные службы, чтобы сохранить себя в базе данных. Но действие этого рассуждения приводит к проблемам сплоченности, сочетания и дублирования. Например, класс Sale теперь должен содержать логику, связанную с обработкой базы данных, например, связанную с SQL и JDBC (Java Database Connectivity). Класс больше не фокусируется на чистой логике приложения "продажа". Теперь другие виды ответственности снижают его сплоченность. Класс должен быть связан с службами технической базы данных другой подсистемы, такими как службы JDBC, а не просто связан с другими объектами в доменном слое программных объектов, поэтому его связь возрастает. И вполне вероятно, что подобная логика базы данных будет дублироваться во многих постоянных классах.

Все эти проблемы указывают на нарушение базового архитектурного принципа: дизайн для разделения основных системных проблем. Храните логику приложения в одном месте (например, объекты программного обеспечения домена), храните логику базы данных в другом месте (например, отдельную подсистему служб сохранения) и т.д., А не смешивайте различные системные проблемы в одном компоненте. [11]

Поддержка разделения основных проблем улучшает сцепление и сцепление в дизайне. Таким образом, даже несмотря на то, что эксперт мог найти какое-то обоснование для того, чтобы возложить ответственность за услуги базы данных в классе Sale, по другим причинам (обычно сплоченность и связь), мы получим плохой дизайн.

Таким образом, SRP обычно превосходит Информационный Эксперт.

Однако принцип инверсии зависимостей может хорошо сочетаться с экспертом. Аргументом здесь будет то, что у Клиента не должно быть зависимости CustomerDTO (общее до подробностей), но наоборот. Это означало бы, что CustomerDTO является экспертом и должен знать, как создать себя с учетом Клиента:

CustomerDTO dto = new CustomerDTO(bob);

Если у вас аллергия на новую, вы можете пойти статично:

CustomerDTO dto = CustomerDTO.buildFor(bob);

Или, если вы ненавидите обоих, мы возвращаемся к AbstractFactory:

public abstract class DTOFactory<D, E> {
    public abstract D createDTO(E entity);
}


public class CustomerDTOFactory extends DTOFactory<CustomerDTO, Customer> {
    @Override
    public CustomerDTO createDTO(Customer entity) {
        return new CustomerDTO(entity);
    }
}

Ответ 6

Я не согласен на 100% с вашими двумя примерами как репрезентативными, но с общей точки зрения вы, кажется, рассуждаете из предположения о двух объектах и ​​только двух объектах.

Если вы еще раз отделите проблему и создадите один (или более) специализированный объект, чтобы взять на себя отдельные обязанности, которые у вас есть, а затем передать управляющий объект другим экземплярам, ​​которые он использует, специализированным объектам, которые вы вырезали вы должны иметь возможность наблюдать счастливый компромисс между SRP (каждая ответственность обрабатывается специализированным объектом) и Tell Do not Ask (контрольный объект сообщает специализированным объектам, которые он составляет вместе, чтобы делать все, что угодно, что они делать, друг к другу).

Это составное решение, которое полагается на какой-то контроллер для координации и делегирования между другими объектами без погрязнения в своих внутренних деталях.