Mam dwie podstawowe klasy Operation
i Trigger
. Każda z nich ma wiele podklas, które specjalizują się w określonych rodzajach operacji lub wyzwalaczy. A Trigger
może wyzwolić określony Operation
. Chociaż Operation
może być wyzwalany przez określony Trigger
.
Muszę napisać kod, który odwzorowuje dane Operation
dane Trigger
(lub odwrotnie), ale nie jestem pewien, gdzie je umieścić.
W takim przypadku kod nie należy wyraźnie do jednej lub drugiej klasy. Jeśli chodzi o zasadę pojedynczej odpowiedzialności, nie jestem pewien, gdzie powinien być kod.
Widzę trzy opcje, które wszystko by działały. Podczas gdy 1 i 2 wydają się być tylko wyborem semantyki, 3 reprezentuje zupełnie inne podejście.
- Na spuście, np
bool Triggers(Operation o)
. - Na operacji, np
bool TriggeredBy(Trigger t)
. - W zupełnie nowej klasie, która zarządza mapowaniem, np
bool MappingExists(Trigger t, Operation o)
.
Jak zdecydować, gdzie umieścić wspólny kod odwzorowania w odniesieniu do zasady pojedynczej odpowiedzialności?
Jak zarządzać pojedynczą odpowiedzialnością, gdy odpowiedzialność jest dzielona?
Edytuj 1.
Rzeczywisty kod wygląda tak. Wszystkie właściwości są albo string
, Guid
, collection<string>
, lub enum
. Zasadniczo są to po prostu małe fragmenty danych.
Edytuj 2.
Przyczyna zwrotu typu bool. Kolejna klasa będzie zużywać kolekcję Trigger
i kolekcję Operation
. Musi wiedzieć, gdzie istnieje mapowanie między a Trigger
, a Operation
. Wykorzysta te informacje do utworzenia raportu.
źródło
Odpowiedzi:
Pomyślałbym o tym w ten sposób: w jaki sposób określa się, która Operacja powoduje uruchomienie wyzwalacza. Musi to być algorytm, który może zmieniać się w czasie lub ewoluować w wiele algorytmów. Umieszczenie go w klasach Trigger lub Operation oznacza, że klasy te będą w stanie obsłużyć takie scenariusze w przyszłości. Zauważ, że nie uważam tego za tak proste jak mapowanie, ponieważ może być więcej.
Moim wyborem byłoby utworzenie klasy za pomocą odpowiednich metod, takich jak GetOperationForTrigger (Trigger t). Pozwala to kodowi przekształcić się w zestaw takich klas, których wybór może zależeć w czasie wykonywania lub innych zmiennych (np. Wzorzec strategii).
Zauważ, że głównym założeniem w tym sposobie myślenia jest pisanie minimalnego kodu (tj. Obecnie trzech klas), ale unikanie poważnego refaktoryzacji, jeśli funkcjonalność będzie musiała zostać rozszerzona w przyszłości, nie zakładając, że zawsze będzie dokładnie jeden sposób określić, który wyzwalacz powoduje, która operacja.
Mam nadzieję że to pomoże. Chociaż odpowiedź jest podobna do user61852, rozumowanie jest inne. W rezultacie implementacja będzie się różnić (tj. Mieć jawne metody zamiast zastępowania równe, więc liczba metod może ewoluować w czasie w zależności od potrzeb).
źródło
Byłem tam, zrobiłem to.
Opcja nr 3.
Nie wiem, jakiego języka będziesz używać, ale użyję pseudokodu, który jest bardzo podobny do języka Java. Jeśli Twoim językiem jest C #, prawdopodobnie masz podobne interfejsy i struktury.
Posiadaj klasę lub interfejs mapowania:
equals()
metodę,Mapping
abyMapping
można było zapytać, czy zawierają one dane mapowanie.equals()
metody.Comparable
, aby móc sortować raporty.Możesz po prostu umieścić mapowania w kolekcji
Później możesz zapytać:
źródło
Obecnie masz klasę A znającą klasę B i klasę B znającą klasę A. To dużo się dzieje.
Z definicji A wykonuje co najmniej własną operację ORAZ sprawdza, czy należy uruchomić B. Odwrotna sytuacja jest prawdą w przypadku B. Jakakolwiek pierwsza klasa nazywa się, powinna być w stanie spojrzeć na wynik i sprawdzić, czy należy uruchomić inne rzeczy.
Spróbuj przerwać to połączenie, dzieląc swoje klasy na mniejsze elementy. Lubię umieszczać komentarz na górze każdej klasy, wyjaśniając zwykłym angielskim, co robi. Jeśli potrzebujesz użyć takich słów jak AND lub ciągnie się ono przez zdanie lub dwa, musisz rozważyć jego podział. Zasadniczo wszystko po „i” powinno należeć do własnej klasy
Sprawdź także, czy możesz zdefiniować interfejs obejmujący funkcje Trigger i Operation. Jeśli nie możesz, to kolejna oznaka, że twoja klasa staje się zbyt duża. To także przerwie połączenie między wami.
źródło
Trigger
byłoby połączone z aOperation
. Ale tak wyglądają dane ze świata rzeczywistego. Są sprzężone, ponieważ istnieje mapowanie, np. Muszą wiedzieć o sobie, aby mieć znaczenie.