Suche…


Details der Strategieimplementierung ausblenden

Eine sehr verbreitete Richtlinie im objektorientierten Design lautet "so wenig wie möglich, aber so viel wie nötig". Dies gilt auch für das Strategiemuster: Es ist normalerweise ratsam, Implementierungsdetails auszublenden, z. B. welche Klassen tatsächlich Strategien implementieren.

Für einfache Strategien, die nicht von externen Parametern abhängen, besteht der gängigste Ansatz darin, die implementierende Klasse selbst als privat (verschachtelte Klassen) oder als Paket-private zu deklarieren und eine Instanz durch ein statisches Feld einer öffentlichen Klasse bereitzustellen:

public class Animal {

  private static class AgeComparator implements Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  }

  // Note that this field has the generic type Comparator<Animal>, *not*
  // Animal.AgeComparator!
  public static final Comparator<Animal> AGE_COMPARATOR = new AgeComparator();

  private final int age;

  Animal(int age) {
    this.age = age;
  }

  public int age() {
    return age;
  }

}

List<Animal> myList = new LinkedList<>();
myList.add(new Animal(10));
myList.add(new Animal(5));
myList.add(new Animal(7));
myList.add(new Animal(9));

Collections.sort(
  myList,
  Animal.AGE_COMPARATOR
);

Das öffentliche Feld Animal.AGE_COMPARATOR definiert eine Strategie, die dann in Methoden wie Collections.sort kann. Der Benutzer muss jedoch nichts über seine Implementierung wissen, nicht einmal über die implementierende Klasse.

Wenn Sie möchten, können Sie eine anonyme Klasse verwenden:

public class Animal {

  public static final Comparator<Animal> AGE_COMPARATOR = new Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  };

  // other members...
}

Wenn die Strategie etwas komplexer ist und Parameter erfordert, werden häufig statische Factory-Methoden wie Collections.reverseOrder(Comparator<T>) . Der Rückgabetyp der Methode sollte keine Implementierungsdetails reverseOrder() , z. B. wird reverseOrder() wie implementiert

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
  // (Irrelevant lines left out.)
  return new ReverseComparator2<>(cmp);
}

Beispiel für ein Strategiemuster in Java mit der Context-Klasse

Strategie:

Strategy ist ein Verhaltensmuster, mit dem der Algorithmus dynamisch von einer Familie verwandter Algorithmen geändert werden kann.

UML des Strategiemusters aus Wikipedia

Geben Sie hier die Bildbeschreibung ein :

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

Problembeschreibung: Bieten Sie 25% Rabatt auf den Artikelpreis für die Monate Juli-Dezember. Geben Sie keinen Rabatt für die Monate Januar bis Juni.

Das obige Beispiel zeigt die Verwendung des Strategy mit Context . Context kann als zentrale Anlaufstelle für den Client .

Zwei Strategien - NoOfferStrategy und QuarterDiscountStrategy wurden gemäß der Problembeschreibung deklariert.

Wie in der Ausgabespalte gezeigt, erhalten Sie einen Rabatt, je nachdem, welchen Monat Sie eingegeben haben

Anwendungsfall (e) für Strategiemuster :

  1. Verwenden Sie dieses Muster, wenn Sie über eine Familie austauschbarer Algorithmen verfügen und den Algorithmus zur Laufzeit ändern müssen.

  2. Halten Sie den Code sauberer, indem Sie bedingte Anweisungen entfernen

Strategiemuster ohne Kontextklasse / Java

Das folgende Beispiel zeigt die Verwendung des Strategiemusters ohne Kontextklasse. Es gibt zwei Implementierungsstrategien, die die Schnittstelle implementieren und das gleiche Problem auf unterschiedliche Weise lösen. Benutzer der EnglishTranslation-Klasse können die Übersetzungsmethode aufrufen und auswählen, welche Strategie sie für die Übersetzung verwenden möchten, indem sie die gewünschte Strategie angeben.

// The strategy interface
public interface TranslationStrategy {
    String translate(String phrase);
}

// American strategy implementation 
public class AmericanTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", bro";
    }
}

// Australian strategy implementation     
public class AustralianTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", mate";
    }
}

// The main class which exposes a translate method
public class EnglishTranslation {

    //  translate a phrase using a given strategy
    public static String translate(String phrase, TranslationStrategy strategy) {
        return strategy.translate(phrase);
    }

    // example usage
    public static void main(String[] args) {

        // translate a phrase using the AustralianTranslationStrategy class
        String aussieHello = translate("Hello", new AustralianTranslationStrategy());
        // Hello, mate

        // translate a phrase using the AmericanTranslationStrategy class    
        String usaHello = translate("Hello", new AmericanTranslationStrategy());
        // Hello, bro
    }
}

Verwenden von Java 8-Funktionsschnittstellen zur Implementierung des Strategiemusters

Dieses Beispiel soll zeigen, wie wir das Strategiemuster mithilfe von Java 8-Funktionsschnittstellen realisieren können. Wir beginnen mit einem einfachen Use-Case-Code in klassischem Java und kodieren ihn anschließend auf Java 8-Weise.

Das von uns verwendete Beispielproblem ist eine Familie von Algorithmen (Strategien), die verschiedene Arten der Kommunikation über eine Distanz beschreiben.

Die klassische Java-Version

Der Vertrag für unsere Familie von Algorithmen wird durch die folgende Schnittstelle definiert:

public interface CommunicateInterface {
    public String communicate(String destination);
}

Dann können wir eine Reihe von Algorithmen wie folgt implementieren:

public class CommunicateViaPhone implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination +" via Phone..";
    }
}

public class CommunicateViaEmail implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Email..";
    }
}

public class CommunicateViaVideo implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Video..";
    }
}

Diese können wie folgt instanziiert werden:

CommunicateViaPhone communicateViaPhone = new CommunicateViaPhone();
CommunicateViaEmail communicateViaEmail = new CommunicateViaEmail();
CommunicateViaVideo communicateViaVideo = new CommunicateViaVideo();

Als Nächstes implementieren wir einen Dienst, der die Strategie verwendet:

public class CommunicationService {
    private CommunicateInterface communcationMeans;

    public void setCommuncationMeans(CommunicateInterface communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

Schließlich können wir die verschiedenen Strategien wie folgt anwenden:

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

Verwendung von Java 8-Funktionsschnittstellen

Der Vertrag der verschiedenen Algorithmusimplementierungen erfordert keine dedizierte Schnittstelle. Stattdessen können wir es mit der vorhandenen java.util.function.Function<T, R> -Schnittstelle beschreiben.

Die verschiedenen Algorithmen, aus denen sich the family of algorithms können als Lambda-Ausdrücke ausgedrückt werden. Dies ersetzt die Strategieklassen und ihre Instantiierungen.

Function<String, String> communicateViaEmail = 
        destination -> "communicating " + destination + " via Email..";
Function<String, String> communicateViaPhone = 
        destination -> "communicating " + destination + " via Phone..";
Function<String, String> communicateViaVideo = 
        destination -> "communicating " + destination + " via Video..";

Als Nächstes können wir den "Dienst" wie folgt codieren:

public class CommunicationService {
    private Function<String, String> communcationMeans;

    public void setCommuncationMeans(Function<String, String> communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

Zum Schluss verwenden wir die Strategien wie folgt

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

Oder auch:

communicationService.setCommuncationMeans(
    destination -> "communicating " + destination + " via Smoke signals.." );
CommunicationService.communicate("anyone");

Strategie (PHP)

Beispiel von www.phptherightway.com

<?php

interface OutputInterface
{
    public function load();
}

class SerializedArrayOutput implements OutputInterface
{
    public function load()
    {
        return serialize($arrayOfData);
    }
}

class JsonStringOutput implements OutputInterface
{
    public function load()
    {
        return json_encode($arrayOfData);
    }
}

class ArrayOutput implements OutputInterface
{
    public function load()
    {
        return $arrayOfData;
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow