Beispiel für das Strategiemuster in der realen Welt


94

Ich habe über die gelesen OCP-Prinzip und wie man das Strategiemuster verwendet, um dies zu erreichen.

Ich wollte versuchen, dies einigen Leuten zu erklären, aber das einzige Beispiel, an das ich denken kann, ist die Verwendung verschiedener Validierungsklassen, basierend auf dem Status einer "Bestellung".

Ich habe ein paar Artikel online gelesen, aber diese beschreiben normalerweise keinen wirklichen Grund, die Strategie anzuwenden, wie das Generieren von Berichten / Rechnungen / Validierungen usw.

Gibt es Beispiele aus der Praxis, bei denen Sie der Meinung sind, dass ein Strategiemuster üblich ist?

Antworten:


99

Was ist damit:

Sie müssen eine Datei verschlüsseln.

Für kleine Dateien können Sie die Strategie "im Speicher" verwenden, bei der die gesamte Datei gelesen und im Speicher gespeichert wird (z. B. für Dateien <1 GB).

Für große Dateien können Sie eine andere Strategie verwenden, bei der Teile der Datei im Speicher gelesen und teilweise verschlüsselte Ergebnisse in tmp-Dateien gespeichert werden.

Dies können zwei verschiedene Strategien für dieselbe Aufgabe sein.

Der Client-Code würde gleich aussehen:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

Das

     Cipher c = CipherFactory.getCipher( file.size() );

Würde die richtige Strategieinstanz für die Verschlüsselung zurückgeben.

Ich hoffe das hilft.

(Ich weiß nicht einmal, ob Chiffre das richtige Wort ist: P)


8
Ist Ihr Beispiel nicht eher ein Fabrikmuster? Ich denke auch, dass es zum Beispiel in C # nicht funktionieren wird. Ihre "getCipher ()" - Methode ist eine statische Methode, aber in C # können Sie keine statische Methode auf einer Schnittstelle definieren (weder in Java denke ich, aber dafür bin ich mir nicht sicher).
FrenchData

10
Sie gehen zusammen. Die Fabrik erstellt die Strategie, aber die Strategie selbst hält den Algorithmus, um die (im Grunde) gleiche Operation auszuführen. Die Strategie kann auch zur Laufzeit geändert werden. Über die Factory-Methode bist du richtig, ich habe es geändert.
OscarRyz

Um Osacars Punkt hinzuzufügen, ohne Fabrik kann dies ohne Fabrik erstellt werden Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder

Stimmen Sie mit @FrenchData überein. Obwohl dies ein gutes Beispiel ist, CipherFactorykann die Präsenz diejenigen verwirren, die mit dem Strategiemuster nicht vertraut sind.
user487772

1
Bei Factory Pattern geht es um Kreation, bei Strategie um Verhalten. Es gibt ein etwas anderes Recht?
nhoxbypass

61

Wieder ein alter Beitrag, der aber immer noch bei der Suche auftaucht, also füge ich zwei weitere Beispiele hinzu (Code ist in C #). Ich liebe das Strategiemuster absolut, da es meinen Hintern oft gerettet hat, wenn die Projektmanager sagen: "Wir möchten, dass die Anwendung 'X' ausführt, aber 'X' ist noch nicht klar und kann sich in naher Zukunft ändern. "" Dieses Video erklärt das Strategiemuster , wird StarCraft als Beispiel verwendet.

Sachen, die in diese Kategorie fallen:

  • Sortieren: Wir möchten diese Zahlen sortieren, wissen aber nicht, ob wir BrickSort, BubbleSort oder eine andere Sortierung verwenden werden

  • Validierung: Wir müssen Elemente gemäß "Einige Regeln" überprüfen, aber es ist noch nicht klar, wie diese Regel aussehen wird, und wir denken möglicherweise an neue.

  • Spiele: Wir möchten, dass der Spieler entweder läuft oder rennt, wenn er sich bewegt, aber vielleicht sollte er in Zukunft auch schwimmen, fliegen, teleportieren, unter der Erde graben usw.

  • Speichern von Informationen: Wir möchten, dass die Anwendung Informationen in der Datenbank speichert, aber später muss sie möglicherweise eine Datei speichern oder einen Webcall durchführen können

  • Ausgabe: Wir müssen X als einfache Zeichenfolge ausgeben, später kann es sich jedoch um eine CSV, XML, JSON usw. handeln.


Beispiele

Ich habe ein Projekt, in dem die Benutzer Produkte Personen in einer Datenbank zuweisen können. Diese Zuordnung eines Produkts zu einer Person hat den Status "Genehmigt" oder "Abgelehnt", was von einigen Geschäftsregeln abhängt. Beispiel: Wenn ein Benutzer einer Person mit einem bestimmten Alter ein Produkt zuweist, sollte sein Status abgelehnt werden. Wenn der Unterschied zwischen zwei Feldern im Element größer als 50 ist, wird der Status usw. abgelehnt.

Zum Zeitpunkt der Entwicklung sind diese Geschäftsregeln noch nicht vollständig geklärt, und es können jederzeit neue Regeln entstehen. Die Stärke des Stragety-Musters besteht darin, dass ich einen RuleAgent erstellt habe, der eine Liste von IRules enthält.

public interface IRule {
    bool IsApproved(Assignment assignment); 
 }

Im Moment der Zuweisung eines Produkts zu einer Person erstelle ich einen RuleAgent, gebe ihm eine Liste von Regeln (die alle IRule implementieren) und fordere ihn auf, eine Zuordnung zu validieren. Es wird alle seine Regeln durchlaufen. Da alle dieselbe Schnittstelle implementieren, haben alle die IsApprovedMethode und geben false zurück, wenn einer von ihnen false zurückgibt.

Wenn jetzt zum Beispiel der Manager plötzlich auftaucht und sagt, wir müssen auch alle Aufgaben an Praktikanten oder alle Aufgaben an Überstunden ablehnen ... Sie machen neue Klassen wie diese:

public OvertimeRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Timesheet >= 40)
        {
            return false;
        }
        return true;
    }
}

public InternRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Title == "Intern")
        {
            return false;
        }
        return true;
    }
}

Sie sehen, dass Sie keine if-Anweisungen oder Code hinzufügen oder entfernen müssen. Erstellen Sie einfach eine neue Regelklasse, die die IRUle-Schnittstelle implementiert, und schalten Sie diese bei Bedarf aus.


Ein weiteres gutes Beispiel: Scott Allens Videoserie unter http://www.asp.net/mvc/pluralsight in der er das Strategiemuster im Unit-Test-Teil der Anwendung verwendet

Er erstellt eine Website mit einer Seite, auf der Artikel nach Beliebtheit angezeigt werden. "Beliebt" kann jedoch viele Dinge sein (die meisten Ansichten, die meisten Abonnenten, das Erstellungsdatum, die meisten Aktivitäten, die geringste Anzahl von Kommentaren usw.), und falls das Management noch nicht genau weiß, wie es bestellt werden soll, und möglicherweise mit anderen experimentieren möchte Bestellungen zu einem späteren Zeitpunkt. Sie erstellen eine Schnittstelle (IOrderAlgorithm oder so) mit einer Bestellmethode und lassen ein Orderer-Objekt die Bestellung an eine konkrete Implementierung der IOrderAlgorithm-Schnittstelle delegieren. Sie können einen "CommentOrderer", "ActivityOrderer" usw. erstellen und diese einfach ausschalten, wenn neue Anforderungen auftauchen.


Ich weiß, dass dies etwas außerhalb des Rahmens der Frage liegt, aber was kommt als nächstes? Wir haben das InternRulejetzt, aber wie lösen wir aus OvertimeRule? Wie stellen wir sicher, dass die OvertimeRule.IsApprovedjetzt aufgerufene Logik auch aufgerufen wird InternRule.IsApproved?
Spencer Ruport

13

Wichtige Hinweise:

  1. Strategie ist Verhaltensmuster. Es wird verwendet, um zwischen einer Familie von Algorithmen zu wechseln.

  2. Dieses Muster enthält eine abstrakte Strategie- Schnittstelle und viele konkrete Strategie-Implementierungen ( Algorithmen ) dieser Schnittstelle.

  3. Die Anwendung verwendet nur die Strategie- Schnittstelle . Abhängig von einigen Konfigurationsparametern wird die konkrete Strategie der Schnittstelle zugeordnet .

UML-Diagramm aus Wikipedia

Geben Sie hier die Bildbeschreibung ein

Ein echtes Wortbeispiel: Fluggesellschaften, die in einigen Monaten (Juli bis Dezember) Rabatte anbieten . Sie können ein Tarifmodul verwenden , das die Preisoptionen abhängig von der Monatszahl festlegt.

Schauen Sie sich ein einfaches Beispiel an. Dieses Beispiel kann auf Online-Einzelhandelsanwendungen erweitert werden, mit denen Warenkorbartikel an bestimmten Tagen / Happy Hour problemlos rabattiert werden können.

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 

   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can use Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }

    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }

}

Ausgabe:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

Nützliche Artikel:

Strategiemuster von dzone

Strategiemuster durch Quellenherstellung


12

Ich kann mir einige ziemlich einfache Beispiele vorstellen:

  • Eine Liste sortieren. Die Strategie ist der Vergleich, mit dem entschieden wird, welches der beiden Elemente in der Liste "Erste" ist.
  • Möglicherweise haben Sie eine Anwendung, in der der Sortieralgorithmus selbst (QuickSort, HeapSort usw.) zur Laufzeit ausgewählt werden kann
  • Anhänge, Layouts und Filter in Log4Net und Log4j
  • Layout-Manager in UI-Toolkits
  • Datenkompression. Möglicherweise haben Sie eine ICompressor-Schnittstelle, deren einzige Methode ungefähr so ​​aussieht:

    Byte [] komprimieren (Byte [] Eingabe);

    Ihre konkreten Komprimierungsklassen können beispielsweise RunLengthCompression, DeflateCompression usw. sein.


9

Eine häufige Verwendung des Strategiemusters besteht darin, benutzerdefinierte Sortierstrategien (in Sprachen ohne Funktionen höherer Ordnung) zu definieren, z. B. eine Liste von Zeichenfolgen nach Länge in Java zu sortieren und dabei eine anonyme innere Klasse zu übergeben (eine Implementierung der Strategie-Schnittstelle):

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

In ähnlicher Weise können Strategien für native Abfragen mit Objektdatenbanken verwendet werden, z. B. in db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});

8

Ich habe eine Anwendung, die ihre Benutzerbasis jeden Tag mit unserem Unternehmensverzeichnis synchronisiert. Benutzer sind aufgrund ihres Status an der Universität berechtigt oder nicht berechtigt. Jeden Tag durchläuft das Bereitstellungsprogramm und stellt sicher, dass diejenigen, die berechtigt sein sollen, in der Anwendung bereitgestellt werden und diejenigen, die nicht de-bereitgestellt werden (tatsächlich nach einem Algorithmus für eine ordnungsgemäße Verschlechterung, aber das ist nebensächlich). Am Samstag mache ich ein gründlicheres Update, das einige Eigenschaften jedes Benutzers synchronisiert und sicherstellt, dass sie die richtige Berechtigung haben. Am Ende des Monats mache ich eine Rückbuchungsverarbeitung basierend auf der Nutzung für diesen Monat.

Ich verwende ein zusammensetzbares Strategiemuster, um diese Synchronisation durchzuführen. Das Hauptprogramm wählt grundsätzlich eine Master-Strategie in Abhängigkeit vom Wochentag (nur Synchronisierungsänderungen / Alle synchronisieren) und der Semesterzeit im Verhältnis zum akademischen Kalender. Wenn der Abrechnungszyklus endet, wird er auch mit einer Abrechnungsstrategie erstellt. Anschließend wird die ausgewählte Strategie über eine Standardschnittstelle ausgeführt.

Ich weiß nicht, wie häufig dies vorkommt, aber ich hatte das Gefühl, dass es perfekt zum Strategiemuster passt.


Dies ist ein sehr gutes Beispiel. Außerdem wird der Unterschied zwischen Befehls- und Strategiemuster auf den Punkt gebracht - die Absicht. "Das Hauptprogramm wählt grundsätzlich je nach Wochentag eine Master-Strategie"
Utsav T

7

Ich weiß, dass dies eine alte Frage ist, aber ich denke, ich habe ein weiteres interessantes Beispiel, das ich kürzlich implementiert habe.

Dies ist ein sehr praktisches Beispiel für das Strategiemuster, das in einem Dokumentenlieferungssystem verwendet wird.

Ich hatte ein PDF-Liefersystem, das ein Archiv mit vielen Dokumenten und einigen Metadaten erhielt. Basierend auf den Metadaten wurde entschieden, wo das Dokument abgelegt werden soll. sagen wir, abhängig von den Daten, konnte ich das Dokument in speichern A, Boder CSpeichersysteme, oder eine Mischung der drei.

Verschiedene Kunden verwendeten dieses System und hatten im Falle von Fehlern unterschiedliche Anforderungen an die Rollback- / Fehlerbehandlung: Man wollte, dass das Liefersystem beim ersten Fehler stoppt, alle bereits gelieferten Dokumente in ihren Lagern belässt, den Prozess jedoch stoppt und nichts anderes liefert ;; Ein anderer wollte, dass es Bbei Fehlern beim Speichern zurückgesetzt wird C, aber alles, was bereits geliefert wurde, belässt A. Es ist leicht vorstellbar, dass ein dritter oder vierter auch andere Bedürfnisse haben wird.

Um das Problem zu lösen, habe ich eine grundlegende Übermittlungsklasse erstellt, die die Übermittlungslogik sowie Methoden zum Zurücksetzen von Daten aus allen Speichern enthält. Diese Methoden werden vom Liefersystem im Fehlerfall nicht direkt aufgerufen. Stattdessen verwendet die Klasse Dependency Injection, um eine "Rollback / Error Handling Strategy" -Klasse (basierend auf dem Kunden, der das System verwendet) zu erhalten, die im Fehlerfall aufgerufen wird und die Rollback-Methoden aufruft, wenn dies für diese Strategie geeignet ist.

Die Übermittlungsklasse selbst meldet, was in der Strategieklasse vor sich geht (welche Dokumente an welche Speicher geliefert wurden und welche Fehler aufgetreten sind), und fragt bei jedem Fehler die Strategie, ob sie fortfahren soll oder nicht. Wenn in der Strategie "stop it" angegeben ist, ruft die Klasse die "cleanUp" -Methode der Strategie auf, die anhand der zuvor gemeldeten Informationen entscheidet, welche Rollback-Methoden von der Übermittlungsklasse aufgerufen werden sollen, oder einfach nichts unternimmt.

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

Ich habe jetzt zwei verschiedene Strategien: eine ist die QuitterStrategy(die beim ersten Fehler beendet wird und nichts bereinigt) und die andere ist die MaximizeDeliveryToAStrategy(die so viel wie möglich versucht, den Prozess nicht abzubrechen und niemals an den Speicher gelieferte Inhalte zurückzusetzen A, aber Rollbacks von, Bwenn die Lieferung Cfehlschlägt).

Nach meinem Verständnis ist dies ein Beispiel für das Strategiemuster. Wenn Sie (ja, Sie lesen) denken, dass ich falsch liege, kommentieren Sie bitte unten und lassen Sie es mich wissen. Ich bin gespannt, was eine "reine" Verwendung des Strategiemusters bedeuten würde und welche Aspekte meiner Implementierung gegen die Definition verstoßen. Ich finde es ein bisschen lustig, weil die Strategie-Oberfläche ein bisschen fett ist. Alle Beispiele, die ich bisher gesehen habe, verwenden nur eine Methode, aber ich denke immer noch, dass dies einen Algorithmus einschließt (wenn ein Teil der Geschäftslogik als Algorithmus betrachtet werden kann, was meiner Meinung nach auch so ist).

Da die Strategie auch während der Ausführungsausführung über Ereignisse informiert wird, kann sie auch als Beobachter betrachtet werden , aber das ist eine andere Geschichte.

Nach ein wenig Recherche scheint es sich um ein "zusammengesetztes Muster" zu handeln (wie MVC, ein Muster, das auf besondere Weise mehrere darunter liegende Entwurfsmuster verwendet), das als Advisor bezeichnet wird . Es ist ein Ratgeber, ob die Lieferung fortgesetzt werden soll oder nicht, aber es ist auch ein aktiver Fehlerbehandler, da es auf Anfrage Rollbacks durchführen kann.

Wie auch immer, dies ist ein ziemlich komplexes Beispiel, das Ihnen das Gefühl geben könnte, dass die Verwendung des Strategiemusters allzu einfach / albern ist. Es kann sehr komplex und noch anwendbarer sein, wenn es zusammen mit anderen Mustern verwendet wird.


6

Strategiemuster werden am häufigsten speziell für Validierungen und Sortieralgorithmen verwendet.

Lassen Sie mich anhand eines einfachen praktischen Beispiels erklären

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

Der Testcode dafür ist

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

Das gleiche Beispiel stammt von http://coder2design.com/strategy-pattern/


Verschiedene Verwendungen von Strategiemustern: Validierungen: Wenn in Ihrem Code viele Validierungen durchgeführt werden müssen. Unterschiedliche Algorithmen: Insbesondere, wenn unterschiedliche Sortieralgorithmen verwendet werden können, z. B. Blasensortierung oder schnelle Sortierung. Speichern von Informationen: Wenn Informationen an verschiedenen Orten angezeigt werden, z. B. in einer Datenbank oder einem Dateisystem. Parsen: Während des Parsens können wir unterschiedliche Strategien für unterschiedliche Eingaben verwenden. Filterstrategien. Layoutstrategien.
Jatinder Pal

5

Ein gutes Beispiel für ein Strategiemuster wäre ein Spiel, in dem wir verschiedene Charaktere haben können und jeder Charakter mehrere Waffen zum Angriff haben kann, aber gleichzeitig nur eine Waffe verwenden kann. Wir haben also den Charakter als Kontext, zum Beispiel König, Kommandant, Ritter, Soldat und Waffe als Strategie, bei der attack () die Methode / der Algorithmus sein kann, die von den verwendeten Waffen abhängt. Wenn die konkreten Waffenklassen also Schwert, Axt, Armbrust, Bogen und Pfeil usw. wären, würden sie alle die attack () -Methode implementieren. Ich bin sicher, dass keine weitere Erklärung erforderlich ist.


1
Ich dachte, dass die akzeptierte Antwort über dieses Beispiel sprechen sollte :)
Jeancarlo Fontalvo

2

Ich habe den Strategieansatz in einer ziemlich komplexen Engine in einer Anwendung verwendet, die ein gutes Beispiel ist. Im Wesentlichen bestand die Aufgabe der Engine darin, zunächst eine Liste der Personen zu finden, die über ein Widget verfügten. Die zweite Aufgabe bestand darin, anhand einer unbekannten Anzahl von Parametern (z. B. Preisentfernung im vorherigen Geschäft) herauszufinden, welche der 10 besten Personen mit einem Widget die besten waren , Lagerbestand, Versandoptionen usw. usw. usw. ...)

Im Wesentlichen haben wir das Problem in zwei Strategien unterteilt: Die erste war das Abrufen von Daten, da wir wussten, dass wir mehrere Quellen für unsere Widgets hatten und in der Lage sein mussten, die Daten abzurufen und in eine gemeinsame Struktur umzuwandeln.

Wir stellten dann auch fest, dass wir mehrere Algorithmen hatten, von denen einige auf der Gewichtung der Parameter beruhten, andere sehr seltsam und eigenwillig waren, und ich konnte ihnen nicht gerecht werden, ohne Visiere und Diagramme herauszuziehen, und nun, Sie haben das Bild, wir hatten viele Algorithmen für Auswahl der besten Leute.

Unser Service selbst war genau das, was er im Wesentlichen die Ein- und Ausgänge definierte und die Daten normalisierte. Außerdem verwendete er ein Anbietermuster, um die anwendungsspezifischen Datenanbieter und Algorithmusanbieter, die die Strategie verwendeten, einzubinden. Dies war ein ziemlich effektives System.

Wir hatten einige Debatten, ob wir eine Strategie oder ein Vorlagenmuster verwendeten, das wir nie gelöst haben.


2

Sind Sie sicher, dass der Status einer "Bestellung" kein Zustandsmuster ist? Ich habe die Vermutung, dass eine Bestellung je nach Status nicht unterschiedlich behandelt wird.

Nehmen Sie zum Beispiel die Methode Schiff auf der Bestellung:

order.Ship();
  • Wenn die Versandart in Abhängigkeit von ihrem Status variiert, haben Sie ein Strategiemuster.
  • Wenn die Ship () -Methode jedoch nur dann erfolgreich ist, wenn die Bestellung bezahlt wurde und die Bestellung noch nicht versendet wurde, haben Sie ein Statusmuster.

Das beste Beispiel für das Zustandsmuster (und andere Muster), das ich gefunden habe, war das Buch " Head First Design Patterns ", das erstaunlich ist. Eine knappe Sekunde wird David Cumps 'Blogging-Reihe von Mustern sein .


2

Angenommen , Sie möchten einen Algorithmus schreiben, um den n-ten X-Tag eines bestimmten Monats und Jahres zu berechnen , z. B. den zweiten Montag im Oktober 2014. Sie möchten die Zeitklasse von Android verwenden android.text.format.Time, um das Datum darzustellen, möchten aber auch einen generischen Algorithmus schreiben das kann auch gelten für java.util.Calendar.

Das habe ich getan.

In DatetimeMath.java:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

In TimeMath.java:

public class TimeMath implements DatetimeMath {
    @Override
    public Object createDatetime(int year, int month, int day) {
        Time t = new Time();
        t.set(day, month, year);
        t.normalize(false);
        return t;
    }

    @Override
    public int getDayOfWeek(Object o) {
        Time t = (Time)o;
        return t.weekDay;
    }   

    @Override
    public void increment(Object o) {
        Time t = (Time)o;
        t.set(t.monthDay + 1, t.month, t.year);
        t.normalize(false);
    }
}

In OrdinalDayOfWeekCalculator.java die Klasse mit dem generischen Algorithmus:

public class OrdinalDayOfWeekCalculator {   
    private DatetimeMath datetimeMath;

    public OrdinalDayOfWeekCalculator(DatetimeMath m) {
        datetimeMath = m;
    }

    public Object getDate(int year, int month, int dayOfWeek, int ordinal) {
        Object datetime = datetimeMath.createDatetime(year, month, 1);
        if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
            return datetime;
        } 
        int xDayCount = 0;
        while (xDayCount != ordinal) {
            datetimeMath.increment(datetime);
            if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
                xDayCount++;
            }
        }
        return datetime;
    }
}

In meiner Android-App würde ich so etwas wie nennen

OrdinalDayOfWeekCalculator odowc = 
        new OrdinalDayOfWeekCalculator(new TimeMath());
Time canadianThanksgiving = (Time)odowc.getDate(
        year, Calendar.OCTOBER, Time.MONDAY, 2);

Wenn ich denselben Algorithmus für wiederverwenden java.util.Calendarmöchte, schreibe ich einfach eine Klasse CalendarMath, die die drei Methoden in DatetimeMath implementiert, und verwende sie dann

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);

2
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

1

Vor einigen Wochen habe ich eine gemeinsame Java-Schnittstelle hinzugefügt, die von einem unserer Domänenobjekte implementiert wurde. Dieses Domänenobjekt wurde aus der Datenbank geladen, und die Datenbankdarstellung war ein Sternschema mit mehr als 10 Zweigen. Eine der Konsequenzen eines so schwergewichtigen Domänenobjekts ist, dass wir andere Domänenobjekte erstellen mussten, die dasselbe Schema repräsentierten, wenn auch weniger schwergewichtig. Also habe ich die anderen leichten Objekte dazu gebracht, dieselbe Schnittstelle zu implementieren. Anders ausgedrückt hatten wir:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

Ursprünglich wollte ich s CollectibleElephantsortieren Elephant. Ziemlich schnell kamen meine Teamkollegen dazu CollectibleElephant, Sicherheitsüberprüfungen durchzuführen , sie zu filtern, wenn sie an die GUI gesendet werden usw.


1

Wir mussten eine Bereitstellungsschnittstelle eines Drittanbieters für eine Unternehmensplattform mit einer sehr komplizierten Datenbank erstellen. Die Übermittlung der bereitzustellenden Daten erfolgte als Liste unserer Datentypen, die in unserer Anwendung in eine Prioritätswarteschlange gestellt wurden, damit sie aufgrund von Abhängigkeiten in der richtigen Reihenfolge in die Datenbank geschrieben werden konnten.

Das Schreiben dieser Daten war dann recht einfach. Stellen Sie sich immer wieder ganz oben in der Prioritätswarteschlange auf und wählen Sie eine Strategie aus, die auf dem Typ des zu extrahierenden Objekts basiert.


0

Aus Wikipedia

Bei der Computerprogrammierung ist das Strategiemuster (auch als Richtlinienmuster bekannt) ein Verhaltenssoftware-Entwurfsmuster, das die Auswahl eines Algorithmus zur Laufzeit ermöglicht. Anstatt einen einzelnen Algorithmus direkt zu implementieren, erhält der Code Laufzeitanweisungen, welche in einer Familie von Algorithmen verwendet werden sollen

In der Windows Paint-Anwendung sehen Sie ein Strategiemuster, in dem Sie Form und Farbe in verschiedenen Abschnitten unabhängig voneinander auswählen können. Hier sind Form und Farbe die Algorithmen, die zur Laufzeit geändert werden können.

Wenn Sie einen Kreis mit roter Farbe zeichnen möchten, anstatt die Option 'RedCircle' bereitzustellen, können Sie den Kreis und eine Farbe Ihrer Wahl auswählen.

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

Ohne Strategiemuster erhöht sich die Anzahl der Klassen mit dem kartesischen Produkt aus Form und Farbe. Auch die Schnittstelle ändert sich für jede Implementierung.


0

Stellen Sie sich zum Beispiel ein Shooter-Spiel mit KI-Feinden vor. Sie möchten, dass sie kontinuierlich auf unterschiedliche Weise kämpfen, je nachdem, was passiert. Mit dem Strategiemuster können Sie kontinuierlich wiederholen und dynamisch ändern, wie eine bestimmte Aktion ausgeführt wird.

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}
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.