"Скажи, не спрашивай" над несколькими объектами домена
Вопрос
Как придерживаться принципа "говори, не спрашивай" при выполнении функции, включающей несколько объектов.
Пример - Генерация отчета
У меня есть следующие объекты (только для иллюстрации):
Автомобиль, Лошадь, Кролик
Между этими объектами нет никакой связи, но я хочу создать отчет на основе этих объектов:
createHtmlReport(Car car, Horse horse, Rabbit rabbit){
Report report = new Report()
report.setSomeField(car.getSerialNumber())
report.setAnotherField(horse.getNumberOfLegs())
// ...etc
}
Проблема этого метода заключается в том, что он должен "извлекать" данные из каждого объекта, что нарушает правило "говори, не спрашивай". Я бы предпочел скрыть внутренности каждого объекта и сделать так, чтобы они генерировали для меня отчет:
car.createHtmlReport()
horse.createHtmlReport()
rabbit.createHtmlReport()
... но тогда я получаю 3 частичных отчета. Кроме того, я не думаю, что Кролик должен знать, как генерировать каждый отдельный отчет, который мне нужен (HTML, JMS, XML, JSON ....).
Наконец, при создании отчета я могу включить несколько элементов:
if (car.getWheels() == 4 || horse.getLegs() == 4)
// do something
4 ответа
Отчет должен поддерживать способность создавать себя.
В этом случае каждый IReportable
Объект должен реализовать void UpdateReport(Report aReport)
,
когда Report.CreateReport(List<Reportable> aList)
вызывается, он перебирает список и каждый объект в своей собственной реализации UpdateReport
вызывает:
aReport.AddCar(serialNumber)
aReport.AddHorse(horseName)
В конце CreateReport
объект отчета должен выдавать свой собственный результат.
Цель правила "Не спрашивай" состоит в том, чтобы помочь вам определить ситуации, когда ответственность, которая должна лежать на данном объекте, в конечном итоге будет реализована за его пределами (что плохо).
Какие обязанности мы можем видеть в вашем случае? Что я вижу это:
1) умение форматировать отчет (в формате xml, ascii, html и т. Д.)
2) зная, что идет на какой отчет
Первый явно не относится к объекту домена (автомобиль, лошадь и т. Д.). Куда 2) идти? Можно предложить доменный объект, но если в вашей системе несколько разных отчетов, вы в конечном итоге обремените свои объекты знаниями о различных деталях отчета, которые будут выглядеть и пахнуть плохо. Не говоря уже о том, что это нарушило бы принцип единой ответственности: быть Кроликом - это одно, а знать, какие части информации о Кролике должны попадать в отчет X против отчета Y, - это совсем другое. Таким образом, я бы разработал классы, которые инкапсулируют содержимое данных, которые поступают в отчет определенного типа (и, возможно, выполняют необходимые вычисления). Я бы не беспокоился о том, что они читают данные членов Кролика, Лошади или Автомобиля. Ответственность, которую реализует этот класс, - это "сбор данных для определенного типа отчета", который, как вы сознательно решили, должен находиться за пределами объекта домена.
Я не знаю точно, как называется этот шаблон (посетитель, строитель, ...):
public interface HorseView {
void showNumberOfLegs(int number);
}
public interface CarView {
void showNumberOfWheels(int number);
void showSerialNumber(String serialNumber);
}
public class Horse {
void show(HorseView view) {
view.showNumberOfLegs(this.numberOfLegs);
}
}
public class Car {
void show(CarView view) {
view.showNumberOfWheels(this.numberOfWheels);
view.showSerialNumber(this.serialNumber);
}
}
public class HtmlReport implements HorseView, CarView {
public void showNumberOfLegs(int number) {
...
}
public void showNumberOfWheels(int number) {
...
}
public void showSerialNumber(String serialNumber) {
...
}
}
public XmlModel implements HorseView, CarView {
...
}
public JsonModel implements HorseView, CarView {
...
}
Таким образом, вы можете иметь несколько представлений одного и того же доменного объекта, не нарушая принцип "Не спрашивай".