Zoeken…


Details van implementatie van strategie verbergen

Een veel voorkomende richtlijn bij objectgeoriënteerd ontwerp is "zo min mogelijk maar zo veel als nodig". Dit is ook van toepassing op het strategiepatroon: meestal is het raadzaam om implementatiedetails te verbergen, bijvoorbeeld welke klassen daadwerkelijk strategieën implementeren.

Voor eenvoudige strategieën die niet afhankelijk zijn van externe parameters, is de meest gebruikelijke aanpak om de implementatieklasse zelf privé te maken (geneste klassen) of pakket-privé en een instantie bloot te leggen via een statisch veld van een openbare klasse:

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

Het openbare veld Animal.AGE_COMPARATOR definieert een strategie die vervolgens kan worden gebruikt in methoden zoals Collections.sort , maar de gebruiker hoeft niets te weten over de implementatie, zelfs niet de implementatieklasse.

Als je wilt, kun je een anonieme klasse gebruiken:

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

Als de strategie een beetje ingewikkelder is en parameters vereist, is het heel gebruikelijk om statische fabrieksmethoden zoals Collections.reverseOrder(Comparator<T>) . Het reverseOrder() van de methode mag geen implementatiedetails onthullen, bijv. reverseOrder() wordt geïmplementeerd zoals

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

Voorbeeld van strategiepatroon in Java met contextklasse

Strategie:

Strategy is een gedragspatroon, waarmee het algoritme dynamisch kan worden gewijzigd uit een familie van gerelateerde algoritmen.

UML van strategiepatroon van Wikipedia

voer hier de afbeeldingsbeschrijving in :

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

output:

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

Probleemstelling: bied 25% korting op de prijs van het artikel voor de maanden juli-december. Geef geen korting voor de maanden januari-juni.

Bovenstaand voorbeeld toont het gebruik van Strategy met Context . Context kan worden gebruikt als Single Point of Contact voor de Client .

Twee strategieën - NoOfferStrategy en QuarterDiscountStrategy zijn per probleemverklaring verklaard.

Zoals weergegeven in de uitvoerkolom, krijgt u korting afhankelijk van de maand die u hebt ingevoerd

Gebruik case (s) voor strategiepatroon :

  1. Gebruik dit patroon wanneer u een reeks uitwisselbare algoritmen hebt en u het algoritme tijdens runtime moet wijzigen.

  2. Houd de code schoner door voorwaardelijke verklaringen te verwijderen

Strategiepatroon zonder contextklasse / Java

Het volgende is een eenvoudig voorbeeld van het gebruik van het strategiepatroon zonder contextklasse. Er zijn twee implementatiestrategieën die de interface implementeren en hetzelfde probleem op verschillende manieren oplossen. Gebruikers van de klasse EnglishTranslation kunnen de vertaalmethode oproepen en kiezen welke strategie ze voor de vertaling willen gebruiken, door de gewenste strategie op te geven.

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

Gebruik van functionele interfaces van Java 8 om het strategiepatroon te implementeren

Het doel van dit voorbeeld is om te laten zien hoe we het strategiepatroon kunnen realiseren met behulp van functionele interfaces van Java 8. We beginnen met een eenvoudige use case-code in het klassieke Java en hercoderen het vervolgens op de Java 8-manier.

Het voorbeeld probleem dat we gebruiken is een familie van algoritmen (strategieën), die verschillende manieren om te communiceren over een afstand te beschrijven.

De klassieke Java-versie

Het contract voor onze familie van algoritmen wordt bepaald door de volgende interface:

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

Dan kunnen we een aantal algoritmen als volgt implementeren:

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..";
    }
}

Deze kunnen als volgt worden geïnstantieerd:

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

Vervolgens implementeren we een service die de strategie gebruikt:

public class CommunicationService {
    private CommunicateInterface communcationMeans;

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

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

Tot slot kunnen we de verschillende strategieën als volgt gebruiken:

CommunicationService communicationService = new CommunicationService();

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

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

Gebruik van Java 8 functionele interfaces

Het contract van de verschillende algoritme-implementaties heeft geen speciale interface nodig. In plaats daarvan kunnen we het beschrijven met behulp van de bestaande java.util.function.Function<T, R> interface.

De verschillende algoritmen die the family of algorithms kunnen worden uitgedrukt als lambda-uitdrukkingen. Dit vervangt de strategieklassen en hun instantiaties.

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..";

Vervolgens kunnen we de "service" als volgt coderen:

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

Ten slotte gebruiken we de strategieën als volgt

CommunicationService communicationService = new CommunicationService();

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

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

Of zelfs:

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

Strategie (PHP)

Voorbeeld van 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow