Ricerca…


Nascondere i dettagli di implementazione della strategia

Una linea guida molto comune nel design orientato agli oggetti è "il meno possibile ma quanto necessario". Ciò vale anche per il modello di strategia: in genere è consigliabile nascondere i dettagli di implementazione, ad esempio quali classi implementano effettivamente le strategie.

Per strategie semplici che non dipendono da parametri esterni, l'approccio più comune consiste nel rendere la classe di implementazione stessa privata (classi nidificate) o package-private e nell'esporre un'istanza attraverso un campo statico di una classe pubblica:

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

Il campo pubblico Animal.AGE_COMPARATOR definisce una strategia che può quindi essere utilizzata in metodi come Collections.sort , ma non richiede all'utente di sapere nulla sulla sua implementazione, nemmeno sulla classe di implementazione.

Se preferisci, puoi usare una classe anonima:

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

Se la strategia è un po 'più complessa e richiede parametri, è molto comune utilizzare metodi di factory static come Collections.reverseOrder(Comparator<T>) . Il tipo di ritorno del metodo non dovrebbe esporre dettagli di implementazione, ad esempio reverseOrder() è implementato come

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

Esempio di modello di strategia in java con classe Context

Strategia:

Strategy è un modello comportamentale, che consente di modificare dinamicamente l'algoritmo da una famiglia di algoritmi correlati.

UML del modello di strategia da Wikipedia

inserisci la descrizione dell'immagine qui :

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

produzione:

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

Dichiarazione di problema: offerta sconto del 25% sul prezzo dell'articolo per i mesi di luglio-dicembre. Non fornire sconti per i mesi di gennaio-giugno.

L'esempio sopra mostra l'utilizzo del modello di Strategy con il Context . Context può essere utilizzato come punto di contatto unico per il Client .

Due strategie: NoOfferStrategy e QuarterDiscountStrategy sono state dichiarate come da dichiarazione del problema.

Come mostrato nella colonna di output, otterrai uno sconto in base al mese che hai inserito

Usa caso / i per modello di strategia :

  1. Utilizzare questo modello quando si dispone di una famiglia di algoritmi intercambiabili e si deve modificare l'algoritmo in fase di esecuzione.

  2. Mantieni il codice più pulito rimuovendo le dichiarazioni condizionali

Modello di strategia senza classe di contesto / Java

Quello che segue è un semplice esempio di utilizzo del modello di strategia senza una classe di contesto. Esistono due strategie di implementazione che implementano l'interfaccia e risolvono lo stesso problema in modi diversi. Gli utenti della classe EnglishTranslation possono chiamare il metodo di traduzione e scegliere la strategia che vorrebbero utilizzare per la traduzione, specificando la strategia desiderata.

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

Utilizzo di interfacce funzionali Java 8 per implementare il modello di strategia

Lo scopo di questo esempio è mostrare come possiamo realizzare il modello della strategia usando le interfacce funzionali Java 8. Inizieremo con i codici di un caso semplice in Java classico e quindi lo ricodificheremo in Java 8.

Il problema di esempio che usiamo è una famiglia di algoritmi (strategie) che descrivono diversi modi di comunicare a distanza.

La versione Java classica

Il contratto per la nostra famiglia di algoritmi è definito dalla seguente interfaccia:

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

Quindi possiamo implementare un numero di algoritmi, come segue:

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

Questi possono essere istanziati come segue:

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

Successivamente, implementiamo un servizio che utilizza la strategia:

public class CommunicationService {
    private CommunicateInterface communcationMeans;

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

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

Infine, possiamo utilizzare le diverse strategie come segue:

CommunicationService communicationService = new CommunicationService();

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

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

Utilizzo di interfacce funzionali Java 8

Il contratto delle diverse implementazioni dell'algoritmo non ha bisogno di un'interfaccia dedicata. Invece, possiamo descriverlo usando l'interfaccia java.util.function.Function<T, R> esistente.

I diversi algoritmi che compongono the family of algorithms possono essere espressi come espressioni lambda. Questo sostituisce le classi di strategia e le loro istanziazioni.

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

Successivamente, possiamo codificare il "servizio" come segue:

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

Finalmente usiamo le strategie come segue

CommunicationService communicationService = new CommunicationService();

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

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

O anche:

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

Strategia (PHP)

Esempio da 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow