Вот упрощенное требование:
Пользователь создает
Question
с несколькимиAnswer
с.Question
должен быть хотя бы одинAnswer
.Уточнение: подумайте
Question
иAnswer
как в тесте : есть один вопрос, но несколько ответов, где немногие могут быть правильными. Пользователь - это актер, который готовит этот тест, поэтому он создает вопросы и ответы.
Я пытаюсь смоделировать этот простой пример таким образом, чтобы: 1) сопоставить реальную модель 2), чтобы быть выразительным с кодом, чтобы минимизировать потенциальное неправильное использование и ошибки, и дать советы разработчикам, как использовать модель.
Вопрос - это сущность , а ответ - значение объекта . Вопрос содержит ответы. Пока у меня есть эти возможные решения.
[A] Фабрика внутриQuestion
Вместо того, чтобы создавать Answer
вручную, мы можем вызвать:
Answer answer = question.createAnswer()
answer.setText("");
...
Это создаст ответ и добавит его к вопросу. Тогда мы можем манипулировать ответом, устанавливая его свойства. Таким образом, только вопросы могут создать ответ. Также мы не даем ответа без вопросов. Тем не менее, мы не можем контролировать создание ответов, поскольку это жестко закодировано в Question
.
Существует также одна проблема с «языком» приведенного выше кода. Пользователь - это тот, кто создает ответы, а не вопрос. Лично мне не нравится, что мы создаем объект значения и, в зависимости от разработчика, заполняем его значениями - как он может быть уверен, что требуется добавить?
[B] Фабрика внутри Вопроса, возьмите № 2
Некоторые говорят, что у нас должен быть такой метод в Question
:
question.addAnswer(String answer, boolean correct, int level....);
Подобно вышеуказанному решению, этот метод берет обязательные данные для ответа и создает тот, который также будет добавлен к вопросу.
Проблема здесь в том, что мы дублируем конструктор Answer
без веской причины. Кроме того, вопрос действительно создает ответ?
[C] Конструктивные зависимости
Давайте будем свободны создавать оба объекта сами. Также давайте выразим право зависимости в конструкторе:
Question q = new Question(...);
Answer a = new Answer(q, ...); // answer can't exist without a question
Это дает подсказки разработчику, так как ответ не может быть создан без вопросов. Однако мы не видим «язык», который говорит, что ответ «добавлен» к вопросу. С другой стороны, нам действительно нужно это увидеть?
[D] Зависимость от конструктора, взять № 2
Мы можем сделать наоборот:
Answer a1 = new Answer("",...);
Answer a2 = new Answer("",...);
Question q = new Question("", a1, a2);
Это противоположная ситуация выше. Здесь ответы могут существовать без вопроса (что не имеет смысла), но вопрос не может существовать без ответа (что имеет смысл). Кроме того , «язык» здесь более ясно по этому вопросу будет иметь ответы.
[E] Общий способ
Это то, что я называю обычным способом, первое, что обычно делает ppl:
Question q = new Question("",...);
Answer a = new Answer("",...);
q.addAnswer(a);
которая является «свободной» версией двух вышеупомянутых ответов, поскольку и ответ, и вопрос могут существовать друг без друга. Нет особого намека на то, что вы должны связать их вместе.
[F] комбинированный
Или я должен объединить C, D, E - чтобы охватить все способы установления отношений, чтобы помочь разработчикам использовать то, что для них лучше.
Вопрос
Я знаю, что люди могут выбрать один из ответов выше, основываясь на «догадке». Но мне интересно, если какой-либо из вышеперечисленных вариантов лучше, чем другой, на то есть веские причины. Кроме того, пожалуйста, не думайте, что внутри вышеупомянутого вопроса, я хотел бы втиснуть здесь некоторые лучшие практики, которые могут быть применены в большинстве случаев - и, если вы согласны, большинство сценариев использования некоторых сущностей похожи. Кроме того, давайте будем независимыми от технологий, например. Я не хочу думать, будет ли использоваться ORM или нет. Просто хочется хорошего, выразительного режима.
Есть ли в этом какая-то мудрость?
РЕДАКТИРОВАТЬ
Пожалуйста, игнорируйте другие свойства Question
и Answer
, они не имеют отношения к вопросу. Я отредактировал приведенный выше текст и изменил большинство конструкторов (при необходимости): теперь они принимают любые необходимые значения свойств. Это может быть просто строка вопроса или карта строк на разных языках, статусы и т. Д. - независимо от того, какие свойства переданы, они не являются фокусом для этого;) Поэтому просто предположим, что мы выше передачи необходимых параметров, если не указано иное. Thanx!
addAnswer
илиassignAnswer
будет лучше, чем простоanswer
, я надеюсь, вы согласны с этим. В любом случае, мой вопрос - вы все равно выбрали бы B и, к примеру, получили копию большинства аргументов в методе ответа? Разве это не было бы дублированием?Answer
- это объект значения, он будет храниться вместе с корнем агрегата своего агрегата. Вы не сохраняете объекты-значения напрямую и не сохраняете сущности, если они не являются корнями их агрегатов.В случае, когда требования настолько просты, что существует множество возможных решений, следует следовать принципу KISS. В вашем случае это будет вариант Е.
Существует также случай создания кода, который выражает что-то, чего не должно быть. Например, связывание создания Ответов на вопрос (A и B) или предоставление ссылки на ответ на вопрос (C и D) добавляет некоторое поведение, которое не является необходимым для домена и может привести к путанице. Кроме того, в вашем случае Вопрос, скорее всего, будет агрегирован с Ответом, а Ответ будет типом значения.
источник
Я бы пошел либо [C], либо [E].
Во-первых, почему не А и Б? Я не хочу, чтобы мой Вопрос отвечал за создание какой-либо связанной ценности. Представьте себе, если у Вопроса есть много других ценностных объектов - вы бы поставили
create
метод для каждого? Или, если есть какие-то сложные агрегаты, тот же случай.Почему бы не [D]? Потому что это противоположно тому, что мы имеем в природе. Сначала мы создаем вопрос. Вы можете представить себе веб-страницу, где вы все это создадите - пользователь сначала создаст вопрос, верно? Следовательно, не D.
[E] это KISS, как сказал @Euphoric. Но я также начинаю любить [C] в последнее время. Это не так запутанно, как кажется. Кроме того, представьте, если Вопрос зависит от большего количества вещей - тогда разработчик должен знать, что ему нужно поместить в Вопрос, чтобы правильно его инициализировать. Хотя вы правы - не существует «визуального» языка, объясняющего, что ответ на самом деле добавляется к вопросу.
Дополнительное чтение
Подобные вопросы заставляют меня задуматься, не являются ли наши компьютерные языки слишком общими для моделирования. (Я понимаю, что они должны быть общими, чтобы ответить на все требования программирования). В последнее время я пытаюсь найти лучший способ выразить бизнес-язык, используя свободные интерфейсы. Примерно так (на языке sudo):
то есть, пытаясь отойти от любых больших * Services и * Repository классов на более мелкие куски бизнес-логики. Просто идея.
источник
Я полагаю, что вы упустили момент, ваш Агрегированный корень должен быть вашим Тестовым объектом.
И если это действительно так, я считаю, что TestFactory лучше всего подходит для решения вашей проблемы.
Вы бы делегировали здание вопросов и ответов на фабрику, и, следовательно, вы могли бы использовать любое решение, о котором думали, не повреждая вашу модель, потому что вы скрываете от клиента то, как вы реализуете свои дочерние объекты.
Это так, если TestFactory - единственный интерфейс, который вы используете для создания экземпляра Test.
источник