Bestes OOP-Entwurfsmuster für eine Abfolge von Operationen


11

Ich arbeite an einer Anwendung, deren Modul die folgenden Finanzoperationen nacheinander ausführt:

Wenn ein Benutzer die Überweisung eines bestimmten Betrags auf sein Bankkonto anfordert:

  1. Überprüfen Sie, ob jetzt eine Transaktion stattfinden kann. (Transaktion kann nur während eines bestimmten Zeitraums durchgeführt werden)
  2. Überprüfen Sie, ob der Benutzer die Abhebung eines Mindestbetrags beantragt hat
  3. Überprüfen Sie, ob der Benutzer über ein Standardkonto verfügt

Das Ergebnis aller oben genannten Aktionen sollte protokolliert werden.

Wenn alle oben genannten Bedingungen erfüllt sind, wird die Transaktion ausgeführt. In Zukunft könnte es einige zusätzliche Überprüfungen geben.

Welches objektorientierte Entwurfsmuster sollte für den obigen Fall am besten geeignet sein?


3
Suchen Sie niemals nach einem Entwurfsmuster, um ein Problem zu lösen. Verwenden Sie Entwurfsmuster, um die richtige Lösung zu kommunizieren. programmers.stackexchange.com/questions/70877/… Befolgen Sie die SOLID-Prinzipien, und Sie werden nicht viel falsch machen.
pdr

2
Ich bin nicht einverstanden. Muster haben Namen, die die Kommunikation erleichtern, und sie sollten auch zur Kommunikation von Lösungen verwendet werden. Aber ich stimme nicht zu "Suchen Sie niemals nach einem Entwurfsmuster, um ein Problem zu lösen". Sie lösen nicht nur spezifische Probleme, sondern befassen sich auch mit unterschiedlichen Kräften und Zwängen. Schauen Sie sich "Proxy" und "Decorator" an. Sie sehen ähnlich aus, lösen aber unterschiedliche Probleme. Bevor Sie ein Problem selbst lösen, sollten Sie sich meiner Meinung nach zumindest bekannte Entwurfsmuster ansehen, um von beiden zu profitieren, einem Standardansatz zur Lösung eines Problems und einer einfachen Möglichkeit, es zu kommunizieren.
Jonny Dee

10
Hier ist eine gute Charakterisierung dessen, was ein Muster ist: "Sie [Muster] bieten funktionierende, konkrete und anpassungsfähige Lösungen für Probleme, die in bestimmten Situationen während der Softwareentwicklung wiederholt auftreten, vom organisatorischen bis zum Programmierkontext." [POSA5, S. 30] Unter diesem Gesichtspunkt ist es völlig klar, dass die Suche nach einem Muster als anpassungsfähige Lösung ein ligitimer Ansatz ist.
Jonny Dee

3
Fragen Sie nach einer objektorientierten Konstruktion, um die einfache alte prozedurale Programmierung zu beschreiben?
Mouviciel

4
Folgen Sie dem KISS-Prinzip. Bisher kann Ihr Problem mit 3 "if" -Anweisungen in einer einzigen Methode gelöst werden. Versuchen Sie nicht, ein Designmuster zu verwenden, nur um cool zu sein. Denken Sie jedes Mal, wenn Sie eine zusätzliche Klasse schreiben, immer: Brauche ich das wirklich?
Eiver

Antworten:


13

Es klingt so, als ob Sie nach einer Kette von Verantwortung suchen . In diesem Fall könnten Sie die folgenden Klassen haben:

  • TransactionValidatorBase abstrakte Basisklasse
  • TransactionTimeValidator
  • TransactionAmountValidator
  • TransactionAccountValidator

Diese sind verkettet, um die von Ihnen angegebenen Regeln anzuwenden.

Weiteres Lesen


11
Mein Verständnis ist, dass die Kette der Verantwortung eher ein Filter ist - dh wir gehen die Kette entlang, bis wir jemanden finden, der für die Bewältigung der Verantwortung gerüstet ist, dann wird dieses "Glied" die Verantwortung übernehmen und aussteigen. Ein Fehler in COR in der Praxis ist, dass es schwierig ist, einen Wert zurückzugeben, den Sie anscheinend dafür benötigen.
Amy Blankenship

Ich denke, Sie können eine einstufige Kette von R haben. Ich denke nicht, dass es schwierig ist, einen Wert aus einer Kette mit ein wenig Abtraktion zurückzugeben. Jede Ebene muss mit einer bestimmten primitiven Schnittstelle kommunizieren und Eingaben von unten akzeptieren, vorausgesetzt, diese Eingabe entspricht der primitiven Schnittstelle. Als Schnittstelle Reichhaltigkeit zwischen zwei Ketten Ebenen erforderlich ist , dass mehr eng gekoppelt sind, kann die abtraction Poly-morphed dass zu unterstützen.
Andyz Smith

2

Das richtige Muster hängt hier wirklich von einem Kontext ab. Bevor ich ein bestimmtes Muster auswähle, werde ich versuchen, Antworten auf diese Fragen zu finden:

  • Müssen zur Laufzeit verschiedene Kombinationen von (1,2,3) Prüfungen erstellt werden?
  • Benötigen sie dieselben Variablen, um ihre Aktionen auszuführen, oder sind sie sehr unterschiedlich?
  • Wie genau sollten die Fehlermeldungen sein?
  • Im Falle eines Fehlers versucht der Benutzer immer, den ersten Schritt zu wiederholen?
  • Wie wird mit Parallelität umgegangen?
  • Fügt jede Methode der Anfrage etwas hinzu oder validiert sie einfach? (Sagen Sie Standard-Konto-ID?)

Basierend auf einem Bauchgefühl würde ich sie als einfache Methoden mit aggregierenden Parametern für Fehlercodes codieren.

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);
}

Es ist möglicherweise eine gute Idee, DoTransaction in die Schnittstelle "ITransactionValidationStragegy" einzufügen und einen Layer-Supertyp zu erstellen, der Validierungs-Boilerplate-Code enthält.

Bei diesem Entwurf gehe ich jedoch davon aus, dass die Validierungslogik zur Kompilierungszeit bestimmt wird.


1

Wenn Ihre Abfolge von Schritten hauptsächlich Validierungsaufgaben erfüllt (wie es aussieht), ohne die Eingaben zu mutieren, würde ich in der Tat an das Muster "Chain of Responsibility" denken, wie in seiner Antwort von @pswg erläutert

Da Ihre Frage jedoch etwas allgemeiner ist, möchte ich auch die "Pipeline-Verarbeitung" hinzufügen, da bei diesem Schritt ein Schritt eine Ausgabe erzeugt, die zur Eingabe für den nächsten Schritt wird (wodurch die ursprüngliche Eingabe mutiert wird). .

Hier sind zwei Artikel darüber:
Pipeline-Sammlung von Martin Fowler
Weitere theoretische Diskussion über das Muster


0

Während die Muster hier bereits erwähnt sind, würde ich Ihnen empfehlen, darüber nachzudenken, wie Sie sie in Ihrer Anwendung verwenden möchten, basierend auf den von Ihnen verwendeten Frameworks.

Beispielsweise wird sich die Validierung, die Sie durchführen möchten, höchstwahrscheinlich im Laufe der Zeit ständig ändern (möglicherweise möchten Sie in Zukunft eine neue Validierung hinzufügen, die Transaktionen auf 10 pro Tag beschränkt). Möglicherweise möchten Sie die Validierung auch nicht durchführen, bevor Ihr eigentlicher Business Service oder Integrationscode aktiviert wird. Es wäre gut, wenn Sie die Validierungen als konfigurierbare hinzufügen könnten.

Wenn Sie Struts verwenden, ist die Verwendung von Interceptors möglicherweise eine gute Idee. Im Frühling bietet die Bohneninjektion als Abhängigkeit mehr Flexibilität. Mein Vorschlag ist, nicht nur die Muster / Redewendungen zu betrachten, sondern auch das Framework, mit dem Sie die Anwendung erstellen, und herauszufinden, wie Sie aus futuristischer Sicht am besten zu Ihren Anforderungen passen können.


-2

Nach meinem Verständnis kann alles, was erforderlich ist, wie unten beschrieben in das Befehlsmuster eingepasst werden. Das Klassendesign kann wie folgt durchgeführt werden.

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

Ihre Client-Klasse enthält das folgende Code-Snippet:

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);

Dies ist nach meinem Verständnis mit dem oben angegebenen Szenario.


Es ist schlecht, denn wenn die Geldvalidierung fehlschlägt, ist die Zeitvalidierung nutzlos, wird aber trotzdem durchgeführt
Ewoks
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.