Najlepszy wzorzec projektowy OOP dla sekwencji operacji

11

Pracuję nad aplikacją, której moduł wykonuje kolejno następujące operacje finansowe:

Gdy użytkownik poprosi o przelanie określonej kwoty na swoje konto bankowe:

  1. sprawdzić, czy jakaś transakcja może się teraz zdarzyć? (transakcja może być przeprowadzona tylko przez określony czas)
  2. sprawdź, czy użytkownik zażądał wypłaty minimalnej kwoty
  3. sprawdź, czy użytkownik ma jakieś domyślne konto

Wynik wszystkich powyższych działań powinien zostać zarejestrowany.

Jeśli wszystkie powyższe warunki zostaną spełnione, transakcja zostanie przeprowadzona. W przyszłości mogą pojawić się dodatkowe kontrole.

Który wzór obiektowy powinien najlepiej pasować do powyższego przypadku?

kumar
źródło
3
Nigdy nie szukaj wzoru, aby rozwiązać problem. Użyj wzorców projektowych, aby przekazać prawidłowe rozwiązanie. programmers.stackexchange.com/questions/70877/… Postępuj zgodnie z zasadami SOLID, a nie popełnisz błędu.
pdr
2
Nie zgadzam się. Wzory mają nazwy ułatwiające komunikację i powinny być również używane do komunikowania rozwiązań. Ale nie zgadzam się z „Nigdy nie szukaj wzoru, aby rozwiązać problem”. Rozwiązują nie tylko określone problemy, ale także radzą sobie z różnymi siłami i ograniczeniami. Zobacz „Proxy” i „Dekorator”. Wyglądają podobnie, ale rozwiązują różne problemy. Tak więc, moim zdaniem, zanim samodzielnie rozwiążesz problem, powinieneś przynajmniej przyjrzeć się dobrze znanym wzorcom projektowym, aby czerpać z nich korzyści, standardowe podejście do rozwiązania problemu i łatwy sposób komunikowania się.
Jonny Dee
10
Oto dobra charakterystyka tego, czym jest wzorzec: „[Wzorce] zapewniają działające, konkretne i elastyczne rozwiązania problemów, które wielokrotnie pojawiają się w pewnych sytuacjach podczas tworzenia oprogramowania, od kontekstów organizacyjnych do programistycznych”. [POSA5, s. 1 30] Zatem z tego punktu widzenia jest całkowicie jasne, że szukanie wzoru jako rozwiązania, które można dostosować, jest podejściem ligatywnym.
Jonny Dee,
3
Czy pytasz o konstrukcję obiektową, aby opisać proste, stare programowanie proceduralne?
mouviciel
4
Postępuj zgodnie z zasadą KISS. Jak dotąd twój problem można rozwiązać za pomocą 3 instrukcji „jeśli” w jednej metodzie. Nie próbuj używać wzoru tylko dlatego, że jest fajny. Za każdym razem, gdy piszesz dodatkowe zajęcia, zawsze myśl: czy naprawdę tego potrzebuję?
Eiver

Odpowiedzi:

13

Wygląda na to, że to, czego szukasz, to łańcuch odpowiedzialności . W takim przypadku możesz mieć następujące klasy:

  • TransactionValidatorBase abstrakcyjna klasa podstawowa
  • TransactionTimeValidator
  • TransactionAmountValidator
  • TransactionAccountValidator

Są one połączone razem, aby zastosować dowolną liczbę określonych reguł.

Furter Reading

pswg
źródło
11
Rozumiem, że Łańcuch Odpowiedzialności jest raczej filtrem - tzn. Schodzimy w dół łańcucha, aż znajdziemy kogoś, kto będzie w stanie poradzić sobie z odpowiedzialnością, wtedy „link” poradzi sobie z odpowiedzialnością i wyjdzie. W praktyce jedną wadą jest to, że trudno jest zwrócić wartość, która wydaje się być do tego potrzebna.
Amy Blankenship
Myślę, że możesz mieć Łańcuch R. na jednym poziomie. Naprawdę nie sądzę, że trudno jest zwrócić wartość z Łańcucha z odrobiną abtrakcji. Każdy poziom będzie musiał komunikować się z pewnym prymitywnym interfejsem i będzie musiał zaakceptować dane wejściowe od dołu, pod warunkiem, że takie dane wejściowe są zgodne z pierwotnym interfejsem. Gdy potrzebne jest bogactwo interfejsu między dwoma poziomami Łańcucha, które są ściślej ze sobą sprzężone, abtraction może zostać zmieniony w sposób wielokształtny, aby to wspierać.
Andyz Smith
3

Jeśli sekwencja kroków polega głównie na sprawdzaniu poprawności (tak, jak się wydaje), bez zmiany danych wejściowych, naprawdę pomyślałbym o wzorcu „łańcucha odpowiedzialności”, jak wyjaśniono w jego odpowiedzi przez @pswg

Ale ponieważ twoje pytanie jest nieco bardziej ogólne, chciałbym również dodać „przetwarzanie potokowe”, ponieważ w tym przypadku krok wytworzyłby dane wyjściowe, które stałyby się danymi wejściowymi dla następnego kroku (w ten sposób mutując oryginalne dane wejściowe) .

Oto dwa artykuły na ten temat:
Kolekcja rurociągów autorstwa Martina Fowlera
Więcej teoretycznych dyskusji na temat wzoru

julio.g
źródło
2

Prawidłowy wzorzec tutaj naprawdę zależy od kontekstu. Przed wybraniem konkretnego wzoru, którego będę się trzymać, postaram się znaleźć odpowiedzi na te pytania:

  • Czy wymagane jest tworzenie różnych kombinacji (1,2,3) kontroli w czasie wykonywania?
  • Czy potrzebują tych samych zmiennych, aby wykonywać swoje działania, czy są bardzo różne?
  • Jak dokładne powinny być komunikaty o błędach?
  • W przypadku niepowodzenia, czy użytkownik zawsze próbuje ponownie od (1) kroku?
  • Jak obsługiwana jest współbieżność?
  • Czy każda metoda dodaje coś do żądania lub po prostu sprawdza? (powiedz Default acct id?)

Opierając się na przeczuciu, kodowałbym je jako proste metody z agregującym parametrem dla kodów błędów.

public void DoTransaction(IErrorAgregator error, TransactionRequest request)
{
    if(!IsTransactionInCertainTimePeriod(request, error)) return;
    if(!IsTransactionAmountInUserBounds(request, error)) return;
    if(!UserHaveDefaultAccount(request, error)) return;
    bankingTransactor.PerformTransaction(request);
}

Dobrym pomysłem może być umieszczenie DoTransaction w interfejsie „ITransactionValidationStragegy” i utworzenie nadtypu warstwy, który będzie zawierał sprawdzający kod sprawdzania poprawności.

Jednak w tym projekcie zakładam, że logika sprawdzania poprawności jest ustalana w czasie kompilacji.

Valera Kolupaev
źródło
0

Chociaż wzorce są już tutaj wspomniane, sugerowałbym, abyś pomyślał o tym, jak chciałbyś użyć tego samego w swojej aplikacji, w oparciu o używane ramy.

Na przykład walidacja, którą chcesz wykonać, najprawdopodobniej będzie się zmieniać wraz z upływem czasu (może być konieczne dodanie nowej walidacji w przyszłości, która ogranicza transakcje do 10 dziennie). Ponadto może nie być konieczne sprawdzanie poprawności przed uruchomieniem faktycznej usługi biznesowej lub kodu integracji. Byłoby dobrze, gdyby można było dodać sprawdzania poprawności jako konfigurowalne.

Jeśli używasz Struts, dobrym pomysłem może być użycie przechwytywaczy. W przypadku wiosny zastrzyk fasoli jako zależność daje większą elastyczność. Moją sugestią jest nie tylko przyjrzenie się wzorcom / idiomom, ale także przyjrzenie się ramom, których używasz do zbudowania aplikacji, i zobaczenie, jak najlepiej możesz dopasować się do swoich wymagań z futurystycznego punktu widzenia.

Bieg
źródło
-2

Według mojego zrozumienia, wszystko, co jest wymagane, można dopasować do wzorca poleceń, jak poniżej. Projektowanie klas można wykonać zgodnie z poniższym opisem.

interface Transaction{
void performAction();
}

class Banking{

void moneyValidation(){
//Validate Here
}

void timeValidation(){
//validate Here
}
}

class TimeValidation implements Transaction{

public Banking bank;

public TimeValidation (Banking bnk){
bank=bnk;
}

void performAction(){
bnk.timeValidation();
}


class MoneyValidation Implements Transaction{

public Banking bank;

public MoneyValidation(Banking bnk;){
bank=bnk;
}

void performAction(){
bnk.moneyValidation();
}
}


class Control{

private List val_list=new ArrayList();

void storeValidation(Transaction trans){
val_list.add(trans);
trans.performAction(val_list.getFirstAndRemove());
}
}

//Same for other validation classes

Twoja klasa klienta będzie zawierać następujący fragment kodu:

Banking bnk = new Banking();
MoneyValidation m_val = new MoneyValidation (bnk);
TimeValidation t_val = new TimeValidation (bnk);
Control ctrl = new Control();
ctrl.storeValidation(m_val);
ctrl.storeValidation(t_val);

Zgodnie z moim zrozumieniem scenariusz ten został podany powyżej.

alok
źródło
Jest to złe, ponieważ gdy nie powiedzie się walidacja pieniędzy, walidacja czasu jest bezużyteczna, ale i tak zostanie wykonana
Ewoks