Mam tę starą implementację wzorca poleceń. To rodzaj przekazywania kontekstu przez całą implementację DIOperation , ale później uświadomiłem sobie, że proces uczenia się i uczenia się (który nigdy się nie kończy) nie jest optymalny. Myślę też, że „odwiedzanie” tutaj nie pasuje i po prostu myli.
Właściwie myślę o refaktoryzacji mojego kodu, również dlatego, że polecenie nie powinno nic wiedzieć o innych, a obecnie wszystkie mają te same pary klucz-wartość. Naprawdę trudno jest utrzymać, która klasa jest właścicielem której klucz-wartość, co czasami prowadzi do zduplikowania zmiennych.
Przykładem przypadku użycia: powiedzmy CommandB wymaga nazwa_użytkownika , która jest tworzona przez CommandA . Czy CommandA powinien ustawić klucz UserNameForCommandB = John ? A może powinny dzielić wspólną wartość UserName = John klucz-wartość? Co jeśli nazwa użytkownika jest używana przez trzecie polecenie?
Jak mogę ulepszyć ten projekt? Dzięki!
class DIParameters {
public:
/**
* Parameter setter.
*/
virtual void setParameter(std::string key, std::string value) = 0;
/**
* Parameter getter.
*/
virtual std::string getParameter(std::string key) const = 0;
virtual ~DIParameters() = 0;
};
class DIOperation {
public:
/**
* Visit before performing execution.
*/
virtual void visitBefore(DIParameters& visitee) = 0;
/**
* Perform.
*/
virtual int perform() = 0;
/**
* Visit after performing execution.
*/
virtual void visitAfter(DIParameters& visitee) = 0;
virtual ~DIOperation() = 0;
};
źródło
Odpowiedzi:
Martwię się trochę o zmienność parametrów poleceń. Czy naprawdę konieczne jest utworzenie polecenia z ciągle zmieniającymi się parametrami?
Problemy z twoim podejściem:
Czy chcesz, aby inne wątki / polecenia zmieniały parametry w trakcie
perform
działania?Chcesz
visitBefore
ivisitAfter
tego samegoCommand
obiektu na miano z różnymiDIParameter
przedmiotami?Czy chcesz, aby ktoś podawał parametry do twoich poleceń, o których polecenia nie mają pojęcia?
Nic nie jest zabronione przez twój obecny projekt. Chociaż ogólna koncepcja parametru klucz-wartość ma swoje zalety, nie podoba mi się to w odniesieniu do ogólnej klasy poleceń.
Przykład konsekwencji:
Rozważ konkretną realizację swojej
Command
klasy - coś w rodzajuCreateUserCommand
. Oczywiście, kiedy poprosisz o utworzenie nowego użytkownika, polecenie będzie wymagało nazwy tego użytkownika. Biorąc pod uwagę, że znamCreateUserCommand
iDIParameters
klasy, który parametr powinienem ustawić?Mógłbym ustawić
userName
parametr lubusername
... czy traktujesz wielkość liter w sposób niewrażliwy? Nie wiedziałbym tak naprawdę… och, czekaj… może to po prostuname
?Jak widać, swobodę, którą zyskujesz dzięki ogólnemu mapowaniu klucz-wartość, oznacza, że używanie twoich klas jako kogoś, kto ich nie zaimplementował, jest nieuzasadnione trudne. Musisz przynajmniej podać stałe dla swoich poleceń, aby inni wiedzieli, które klucze są obsługiwane przez to polecenie.
Możliwe różne podejścia do projektu:
Parameter
instancje na niezmienne, możesz swobodnie wykorzystywać je między różnymi poleceniami.UserParameter
klasę, która zawiera dokładnie parametry, których potrzebowałbym dla poleceń angażujących użytkownika, znacznie łatwiej byłoby pracować z tym interfejsem API. Nadal możesz mieć dziedziczenie parametrów, ale nie ma sensu, aby klasy poleceń przyjmowały dowolne parametry - po stronie pro oznacza to oczywiście, że użytkownicy interfejsu API wiedzą, które parametry są dokładnie wymagane.visitBefore
ivisitAfter
, jednocześnie ponowne ich z różnymi parametrami, będzie otwarty na problem coraz wywołana z różnych parametrów. Jeśli parametry powinny być takie same w przypadku wielu wywołań metod, należy umieścić je w komendzie, aby nie można było ich zmienić na inne parametry między wywołaniami.źródło
Zaletą zasad projektowania jest to, że prędzej czy później powodują one konflikt między sobą.
W opisanej sytuacji myślę, że wolałbym zastosować pewien kontekst, w którym każde polecenie może pobierać informacje i umieszczać informacje (szczególnie jeśli są to pary klucz-wartość). Jest to oparte na kompromisie: nie chcę, aby osobne polecenia były łączone tylko dlatego, że są one pewnego rodzaju danymi wejściowymi do siebie. W CommandB nie obchodzi mnie, jak ustawiono UserName - wystarczy, że mogę z niego korzystać. To samo w CommandA: wprowadzam informacje, nie chcę wiedzieć, co inni z tym robią - ani kim są.
Oznacza to rodzaj przejściowego kontekstu, który można znaleźć źle. Dla mnie alternatywa jest gorsza, zwłaszcza jeśli ten prosty kontekst klucz-wartość (może być zwykłą fasolą z modułami pobierającymi i ustawiającymi, aby nieco ograniczyć współczynnik „swobodnej postaci”), może pozwolić, aby rozwiązanie było proste i testowalne, z dobrze oddzielne polecenia, każde z własną logiką biznesową.
źródło
Załóżmy, że masz interfejs poleceń:
I temat:
Potrzebujesz:
Ustaw
NameObserver& o
jako odniesienie do CommandB. Teraz za każdym razem, gdy CommandA zmienia nazwę podmiotów CommandB może wykonać z poprawnymi informacjami. Jeśli nazwa jest używana przez więcej poleceń, użyj astd::list<NameObserver>
źródło
Nie wiem, czy jest to właściwy sposób, aby poradzić sobie z tym na programistach (w takim przypadku przepraszam), ale po sprawdzeniu wszystkich odpowiedzi tutaj (w szczególności @ Frank). Kod zreorganizowałem w ten sposób:
Dzięki za pomoc i mam nadzieję, że nie popełniłem tutaj błędów :)
źródło