Oto uproszczony wymóg:
Użytkownik tworzy
Question
z wielomaAnswer
s.Question
musi mieć co najmniej jedenAnswer
.Wyjaśnienie: pomyśl
Question
iAnswer
jak w teście : jest jedno pytanie, ale kilka odpowiedzi, z których kilka może być poprawnych. Użytkownik jest aktorem, który przygotowuje ten test, dlatego tworzy pytania i odpowiedzi.
Staram się modelować ten prosty przykład, aby 1) dopasować do rzeczywistego modelu 2), aby był wyrazisty z kodem, aby zminimalizować potencjalne niewłaściwe użycie i błędy oraz dać wskazówki programistom, jak korzystać z modelu.
Pytanie jest bytem , a odpowiedź jest przedmiotem wartości . Pytanie zawiera odpowiedzi. Do tej pory mam te możliwe rozwiązania.
[A] Fabryka w środkuQuestion
Zamiast tworzyć Answer
ręcznie, możemy wywołać:
Answer answer = question.createAnswer()
answer.setText("");
...
To stworzy odpowiedź i doda ją do pytania. Następnie możemy manipulować odpowiedzią, ustawiając jej właściwości. W ten sposób tylko pytania mogą stworzyć odpowiedź. Ponadto zapobiegamy otrzymywaniu odpowiedzi bez pytania. Jednak nie mamy kontroli nad tworzeniem odpowiedzi, ponieważ jest to na stałe zapisane w Question
.
Istnieje również jeden problem z „językiem” powyższego kodu. Użytkownik to ten, który tworzy odpowiedzi, a nie pytanie. Osobiście nie lubię, gdy tworzymy obiekt wartości i zależnie od programisty, aby wypełniał go wartościami - skąd może być pewien, co należy dodać?
[B] Fabryka wewnątrz pytania, weź nr 2
Niektórzy twierdzą, że powinniśmy zastosować tego rodzaju metodę w Question
:
question.addAnswer(String answer, boolean correct, int level....);
Podobnie jak w powyższym rozwiązaniu, ta metoda pobiera dane obowiązkowe dla odpowiedzi i tworzy takie, które również zostaną dodane do pytania.
Problem polega na tym, że powielamy konstruktora Answer
bez powodu. Czy pytanie naprawdę tworzy odpowiedź?
[C] Zależności konstruktora
Dajmy sobie swobodę w tworzeniu obu obiektów. Wyraźmy również zależność bezpośrednio w konstruktorze:
Question q = new Question(...);
Answer a = new Answer(q, ...); // answer can't exist without a question
Daje to wskazówki dla programistów, ponieważ odpowiedzi nie można utworzyć bez pytania. Nie widzimy jednak „języka”, który mówi, że odpowiedź jest „dodana” do pytania. Z drugiej strony, czy naprawdę musimy to zobaczyć?
[D] Zależność konstruktora, weź # 2
Możemy zrobić odwrotnie:
Answer a1 = new Answer("",...);
Answer a2 = new Answer("",...);
Question q = new Question("", a1, a2);
Jest to sytuacja odwrotna do powyższej. Tutaj odpowiedzi mogą istnieć bez pytania (co nie ma sensu), ale pytanie nie może istnieć bez odpowiedzi (co ma sens). Również „język” tutaj jest bardziej przejrzysty, ponieważ pytanie będzie zawierać odpowiedzi.
[E] Wspólny sposób
To, co nazywam powszechnym sposobem, pierwszą rzeczą, którą zwykle robią ppl:
Question q = new Question("",...);
Answer a = new Answer("",...);
q.addAnswer(a);
która jest „luźną” wersją dwóch powyższych odpowiedzi, ponieważ zarówno odpowiedź, jak i pytanie mogą istnieć bez siebie. Nie ma żadnej szczególnej wskazówki, że trzeba je połączyć.
[F] Połączone
A może powinienem łączyć C, D, E - aby objąć wszystkie sposoby tworzenia relacji, aby pomóc programistom w użyciu tego, co jest dla nich najlepsze.
Pytanie
Wiem, że ludzie mogą wybrać jedną z powyższych odpowiedzi na podstawie „przeczucia”. Zastanawiam się jednak, czy którykolwiek z powyższych wariantów jest lepszy od drugiego z uzasadnionego powodu. Proszę również nie myśleć w ramach powyższego pytania, chciałbym tu przytoczyć najlepsze praktyki, które można zastosować w większości przypadków - a jeśli się zgadzacie, większość przypadków użycia tworzenia niektórych podmiotów jest podobnych. Bądźmy tu także agnostycy technologiczni, np. Nie chcę myśleć, czy ORM będzie używany, czy nie. Po prostu chcę dobrego, ekspresyjnego trybu.
Jakaś mądrość na ten temat?
EDYTOWAĆ
Zignoruj inne właściwości Question
i Answer
, nie są one istotne dla pytania. Zredagowałem powyższy tekst i zmieniłem większość konstruktorów (tam, gdzie było to potrzebne): teraz akceptują wszelkie niezbędne potrzebne wartości właściwości. Może to być po prostu ciąg zapytania lub mapa ciągów w różnych językach, statusach itp. - niezależnie od przekazywanych właściwości, nie są one w centrum uwagi;) Załóżmy więc, że przekraczamy niezbędne parametry, chyba że podano inaczej. Dzięki!
źródło
addAnswer
lubassignAnswer
byłby lepszym językiem niż tylkoanswer
, mam nadzieję, że się z tym zgadzasz. W każdym razie moje pytanie brzmi - czy nadal wybrałbyś B i np. Miałbyś kopię większości argumentów w metodzie odpowiedzi? Czy nie byłoby to duplikowaniem?Answer
to obiekt wartości, który będzie przechowywany z głównym agregatem jego agregatu. Nie przechowujesz bezpośrednio obiektów wartości, ani nie zapisujesz encji, jeśli nie są one pierwiastkami ich agregatów.W przypadku gdy wymagania są tak proste, że istnieje wiele możliwych rozwiązań, należy przestrzegać zasady KISS. W twoim przypadku byłaby to opcja E.
Istnieje również przypadek stworzenia kodu, który wyraża coś, czego nie powinien. Na przykład wiązanie tworzenia odpowiedzi na pytanie (A i B) lub odniesienie odpowiedzi na pytanie (C i D) dodaje pewne zachowanie, które nie jest konieczne w domenie i może być mylące. Również w twoim przypadku Pytanie najprawdopodobniej zostanie połączone z odpowiedzią, a odpowiedź będzie typem wartości.
źródło
Wybrałbym [C] lub [E].
Po pierwsze, dlaczego nie A i B? Nie chcę, aby moje Pytanie było odpowiedzialne za tworzenie powiązanych wartości. Wyobraź sobie, że Pytanie ma wiele innych obiektów wartości - czy umieściłbyś
create
metodę dla każdego z nich? Lub jeśli istnieją jakieś złożone agregaty, ten sam przypadek.Dlaczego nie [D]? Ponieważ jest odwrotnie niż w naturze. Najpierw tworzymy pytanie. Możesz sobie wyobrazić stronę internetową, na której to wszystko tworzysz - użytkownik najpierw utworzyłby pytanie, prawda? Dlatego nie D.
[E] to KISS, jak powiedział @Euphoric. Ale ostatnio też polubiłem [C]. To nie jest tak mylące, jak się wydaje. Co więcej, wyobraź sobie, że pytanie zależy od większej liczby rzeczy - wtedy programista musi wiedzieć, co musi umieścić w pytaniu, aby zostało poprawnie zainicjowane. Chociaż masz rację - nie ma języka „wizualnego” wyjaśniającego, że odpowiedź jest dodawana do pytania.
Dodatkowe lektury
Takie pytania sprawiają, że zastanawiam się, czy nasze języki komputerowe nie są zbyt ogólne do modelowania. (Rozumiem, że muszą być ogólne, aby odpowiedzieć na wszystkie wymagania programowe). Ostatnio próbuję znaleźć lepszy sposób na wyrażenie języka biznesowego przy użyciu płynnych interfejsów. Coś takiego (w języku sudo):
tj. próba odejścia od dużych * usług i * klas repozytoriów do mniejszych części logiki biznesowej. Po prostu pomysł.
źródło
Uważam, że nie zauważyłeś tutaj żadnego punktu, Twój główny katalog zagregowany powinien być twoją jednostką testową.
A jeśli tak jest naprawdę, uważam, że TestFactory najlepiej nadaje się do rozwiązania twojego problemu.
Delegowałbyś budynek pytań i odpowiedzi do fabryki, a zatem mógłbyś w zasadzie użyć dowolnego rozwiązania, o którym pomyślałeś, nie uszkadzając swojego modelu, ponieważ ukrywasz się przed klientem tak, jak tworzysz instancje podrzędne.
Jest to tak długo, jak TestFactory jest jedynym interfejsem używanym do tworzenia wystąpienia testu.
źródło