Esempio del mondo reale del modello di strategia


95

Ho letto del principale OCP e di come utilizzare il modello di strategia per ottenere questo risultato.

Volevo provare a spiegarlo a un paio di persone, ma l'unico esempio a cui riesco a pensare è l'utilizzo di classi di convalida diverse in base allo stato di un "ordine".

Ho letto un paio di articoli online, ma questi di solito non descrivono un motivo reale per utilizzare la strategia, come la generazione di rapporti / fatture / convalida ecc ...

Ci sono esempi del mondo reale in cui pensi che un modello strategico sia comune?

Risposte:


100

Che dire di questo:

Devi crittografare un file.

Per file piccoli, puoi usare la strategia "in memory", dove il file completo viene letto e mantenuto in memoria (diciamo per file <1 GB)

Per file di grandi dimensioni, è possibile utilizzare un'altra strategia, in cui parti del file vengono lette in memoria e i risultati parziali crittografati vengono archiviati in file tmp.

Queste possono essere due strategie diverse per lo stesso compito.

Il codice client sarebbe lo stesso:

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

}

Il

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

Restituirebbe l'istanza della strategia corretta per il codice.

Spero che aiuti.

(Non so nemmeno se Cipher sia la parola giusta: P)


8
Il tuo esempio non è più un Factory Pattern? Inoltre penso che non funzionerà in C # per esempio. Il tuo metodo "getCipher ()" è un metodo statico ma in C # non puoi definire un metodo statico su un'interfaccia (nemmeno in Java credo, ma per questo non sono sicuro).
FrenchData

10
Vanno insieme. La Factory crea la strategia, ma la strategia auto tiene l'algoritmo per eseguire (sostanzialmente) la stessa operazione. La strategia può essere modificata anche in fase di esecuzione. Riguardo al metodo di fabbrica hai ragione, l'ho cambiato.
OscarRyz

Per aggiungere il punto Osacars, senza fabbrica questo può essere creato senza fabbrica Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder

D'accordo con @FrenchData. Pur essendo un ottimo esempio, la presenza di CipherFactorypuò confondere coloro che non hanno familiarità con il modello strategico.
user487772

1
Il modello di fabbrica riguarda la creazione, la strategia riguarda il comportamento. C'è una leggera differenza, giusto?
nhoxbypass

62

Ancora una volta, un vecchio post ma viene ancora visualizzato nelle ricerche, quindi aggiungerò altri due esempi (il codice è in C #). Adoro il pattern Strategy poiché mi ha salvato il culo molte volte quando i project manager dicono: "Vogliamo che l'applicazione faccia 'X', ma 'X' non è ancora chiaro e può cambiare nel prossimo futuro. " Questo video che spiega il modello strategico , usa StarCraft come esempio.

Cose che rientrano in questa categoria:

  • Ordinamento: vogliamo ordinare questi numeri, ma non sappiamo se useremo BrickSort, BubbleSort o qualche altro ordinamento

  • Convalida: Dobbiamo controllare gli elementi in base a "Alcune regole", ma non è ancora chiaro quale sarà quella regola e potremmo pensare a nuove.

  • Giochi: Vogliamo che il giocatore cammini o corra quando si muove, ma forse in futuro dovrebbe anche essere in grado di nuotare, volare, teletrasportarsi, scavare sottoterra, ecc.

  • Memorizzazione delle informazioni: vogliamo che l'applicazione memorizzi le informazioni nel database, ma in seguito potrebbe essere necessario essere in grado di salvare un file o effettuare una chiamata web

  • Output: dobbiamo produrre X come una stringa semplice, ma in seguito potrebbe essere un CSV, XML, JSON, ecc.


Esempi

Ho un progetto in cui gli utenti possono assegnare prodotti a persone in un database. Questa assegnazione di un prodotto a una persona ha uno stato che è "Approvato" o "Rifiutato", che dipende da alcune regole aziendali. Ad esempio: se un utente assegna un prodotto a una persona con una certa età, il suo stato dovrebbe essere rifiutato; Se la differenza tra due campi nell'elemento è maggiore di 50, il suo stato viene rifiutato, ecc.

Ora, al momento dello sviluppo, queste regole aziendali non sono ancora del tutto chiare e potrebbero emergere nuove regole in qualsiasi momento. Il potere dello stragety-pattern è che ho creato un RuleAgent, a cui viene fornito un elenco di IRules.

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

Al momento dell'assegnazione di un prodotto a una persona, creo un RuleAgent, gli do un elenco di regole (che implementano tutte IRule) e gli chiedo di convalidare un'assegnazione. Correrà attraverso tutte le sue regole. Il che, poiché implementano tutti la stessa interfaccia, hanno tutti il IsApprovedmetodo e restituiscono false se qualcuno di essi restituisce false.

Ora, quando, ad esempio, il manager improvvisamente si avvicina e dice, dobbiamo anche rifiutare tutti gli incarichi agli stagisti o tutti gli incarichi a persone che fanno gli straordinari ... Crei nuovi corsi come questo:

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

Si vede che non è necessario continuare ad aggiungere o rimuovere istruzioni if ​​o codice, basta creare una nuova classe di regole che implementa l'interfaccia IRUle e disattivarli quando necessario.


Un altro ottimo esempio: la serie di video di Scott Allen su http://www.asp.net/mvc/pluralsight dove utilizza il modello di strategia nella parte di test unitario dell'applicazione

Realizza un sito web che ha una pagina che mostra gli articoli in base alla popolarità. Tuttavia "Popolare" può essere molte cose (la maggior parte delle visualizzazioni, la maggior parte degli iscritti, la data di creazione, la maggior parte delle attività, il minor numero di commenti, ecc.) E, nel caso in cui la gestione del caso non sappia ancora esattamente come ordinare e potrebbe voler sperimentare con diversi ordini in un secondo momento. Si crea un'interfaccia (IOrderAlgorithm o qualcosa del genere) con un metodo order e si lascia che un oggetto Orderer deleghi l'ordinamento a un'implementazione concreta dell'interfaccia IOrderAlgorithm. Puoi creare un "CommentOrderer", "ActivityOrderer", ecc ... E semplicemente disattivarli quando emergono nuovi requisiti.


So che questo è un po 'fuori portata della domanda, ma cosa viene dopo? Ora abbiamo questo, InternRulema come ci stiamo attivando OvertimeRule? Come ci assicuriamo che qualunque logica chiamata OvertimeRule.IsApprovedora chiami anche InternRule.IsApproved?
Spencer Ruport

13

Note chiave:

  1. La strategia è un modello di progettazione comportamentale. Viene utilizzato per passare da una famiglia di algoritmi.

  2. Questo modello contiene un'interfaccia di strategia astratta e molte implementazioni di strategia concreta ( algoritmi ) di tale interfaccia.

  3. L'applicazione utilizza solo l' interfaccia della strategia . A seconda di alcuni parametri di configurazione, la strategia concreta verrà contrassegnata per l' interfaccia .

Diagramma UML da wikipedia

inserisci qui la descrizione dell'immagine

Un vero esempio di parola: compagnie aeree che offrono sconti durante alcuni mesi (luglio-dicembre) . Puoi avere un modulo Tariffa , che decide le opzioni di prezzo in base al numero del mese.

Dai un'occhiata a un semplice esempio. Questo esempio può essere esteso alle applicazioni di vendita al dettaglio in linea, che offrono facilmente sconti sugli articoli del carrello in giorni speciali / happy hour.

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

}

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

Articoli utili:

modello di strategia di dzone

modello di strategia per fonte


12

Posso pensare a diversi esempi abbastanza semplici:

  • Ordinamento di un elenco. La strategia è il confronto utilizzato per decidere quale dei due elementi nell'elenco è "Primo"
  • Potresti avere un'applicazione in cui l'algoritmo di ordinamento stesso (QuickSort, HeapSort, ecc.) Può essere scelto in fase di esecuzione
  • Appendici, layout e filtri in Log4Net e Log4j
  • Gestori di layout nei toolkit dell'interfaccia utente
  • Compressione dati. Potresti avere un'interfaccia ICompressor il cui unico metodo assomiglia a questo:

    byte [] compress (byte [] input);

    Le tue classi di compressione del calcestruzzo potrebbero essere cose come RunLengthCompression, DeflateCompression, ecc.


9

Un utilizzo comune del modello di strategia è definire strategie di ordinamento personalizzate (in linguaggi senza funzioni di ordine superiore), ad esempio per ordinare un elenco di stringhe in base alla lunghezza in Java, passando una classe interna anonima (un'implementazione dell'interfaccia della strategia):

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 modo simile, le strategie possono essere utilizzate per query native con database di oggetti, ad esempio in db4o:

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

8

Ho un'applicazione che sincronizza la sua base di utenti ogni giorno con la nostra directory aziendale. Gli utenti sono idonei o non idonei in base al loro stato nell'Università. Ogni giorno il programma di provisioning viene eseguito e si assicura che coloro che dovrebbero essere idonei vengano sottoposti a provisioning nell'applicazione e quelli che non lo sono vengono annullati (in realtà secondo un grazioso algoritmo di degradazione, ma non è questo il punto). Sabato eseguo un aggiornamento più approfondito che sincronizza alcune proprietà di ogni utente e mi assicura che abbiano la giusta idoneità. Alla fine del mese eseguo un po 'di elaborazione della fattura in base all'utilizzo per quel mese.

Uso un modello di strategia componibile per eseguire questa sincronizzazione. Il programma principale sceglie fondamentalmente una strategia principale a seconda del giorno della settimana (sincronizza solo le modifiche / sincronizza tutto) e l'ora del semestre rispetto al calendario accademico. Se il ciclo di fatturazione sta terminando, lo compone anche con una strategia di fatturazione. Quindi esegue la strategia scelta tramite un'interfaccia standard.

Non so quanto sia comune, ma mi sembrava che fosse perfetto per il modello strategico.


Questo è un ottimo esempio. Inoltre, ti dice chiaramente la differenza tra il modello di comando e quello strategico in poche parole: l'intento. "Il programma principale sceglie fondamentalmente una strategia principale a seconda del giorno della settimana"
Utsav T

7

So che questa è una vecchia domanda, ma penso di avere un altro esempio interessante che ho implementato di recente.

Questo è un esempio molto pratico del modello di strategia utilizzato in un sistema di consegna dei documenti.

Avevo un sistema di consegna PDF che riceveva un archivio contenente molti documenti e alcuni metadati. Sulla base dei metadati, ha deciso dove inserire il documento; per esempio, a seconda dei dati, ho potuto conservare il documento in A, Bo Csistemi di storage, o una combinazione dei tre.

Diversi clienti utilizzavano questo sistema e avevano diversi requisiti di rollback / gestione degli errori in caso di errori: uno voleva che il sistema di consegna si fermasse al primo errore, lasciasse tutti i documenti già consegnati nei loro magazzini, ma interrompesse il processo e non consegnasse altro ; un altro voleva che venisse eseguito il rollback Bin caso di errori durante l'archiviazione C, ma lasciare tutto ciò che era già stato consegnato a A. È facile immaginare che anche un terzo o un quarto avrà esigenze diverse.

Per risolvere il problema, ho creato una classe di consegna di base che contiene la logica di consegna, oltre a metodi per ripristinare le cose da tutti gli archivi. Questi metodi non vengono effettivamente chiamati direttamente dal sistema di consegna in caso di errori. Invece, la classe utilizza Dependency Injection per ricevere una classe "Rollback / Error Handling Strategy" (basata sul cliente che utilizza il sistema), che viene chiamata in caso di errori, che a sua volta chiama i metodi di rollback se è appropriato per quella strategia.

La stessa classe di consegna segnala cosa sta succedendo alla classe di strategia (quali documenti sono stati consegnati a quali archivi e quali errori si sono verificati) e ogni volta che si verifica un errore, chiede alla strategia se continuare o meno. Se la strategia dice "stop it", la classe chiama il metodo "cleanUp" della strategia, che utilizza le informazioni precedentemente riportate per decidere quali metodi di rollback chiamare dalla classe di consegna, o semplicemente non fare nulla.

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

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

Quindi ora ho due diverse strategie: una è la QuitterStrategy(che si chiude al primo errore e non ripulisce nulla) e l'altra è la MaximizeDeliveryToAStrategy(che cerca il più possibile di non interrompere il processo e non ripristinare mai le cose consegnate in archivio A, ma esegue il rollback delle cose da Bse la consegna a Cnon riesce).

A quanto mi risulta, questo è un esempio del modello strategico. Se tu (sì, stai leggendo) pensi che mi sbagli, per favore commenta qui sotto e fammi sapere. Sono curioso di sapere cosa costituirebbe un uso "puro" del modello di strategia e quali aspetti della mia implementazione violano la definizione. Penso che sia un po 'divertente perché l'interfaccia strategica è un po' grassa. Tutti gli esempi che ho visto finora utilizzano un solo metodo, ma penso ancora che questo incapsuli un algoritmo (se un pezzo di logica aziendale può essere considerato un algoritmo, cosa che penso lo faccia).

Poiché la strategia viene notificata anche sugli eventi durante l'esecuzione della consegna, può anche essere considerata un osservatore , ma questa è un'altra storia.

Da una piccola ricerca, sembra che questo sia un "pattern composito" (come MVC, un pattern che utilizza più pattern di progettazione sottostanti in un modo particolare) chiamato Advisor . È un consigliere sul fatto che la consegna debba continuare o meno, ma è anche un gestore di errori attivo poiché può eseguire il rollback di cose quando richiesto.

Ad ogni modo, questo è un esempio piuttosto complesso che potrebbe farti sentire che gli usi del modello strategico sono fin troppo semplici / sciocchi. Può essere davvero complesso e persino più applicabile se usato insieme ad altri modelli.


6

Il modello di strategia è il modello più comunemente usato specialmente per le convalide e gli algoritmi di ordinamento.

Mi spiego con un semplice esempio pratico

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

}  

Il codice di test per questo è

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

}

Lo stesso esempio è tratto da http://coder2design.com/strategy-pattern/


Diversi usi del modello di strategia: Validazioni: quando ci sono molte convalide, è necessario eseguire il codice. Algoritmi diversi: Specialmente quando possono essere utilizzati algoritmi di ordinamento diversi, ad esempio l'ordinamento a bolle o l'ordinamento rapido. Memorizzazione di informazioni: quando potremmo informazioni in luoghi diversi, ad esempio database o file system. Parsing: durante l'analisi possiamo utilizzare strategie diverse per input diversi. Strategie di filtraggio. Strategie di layout.
Jatinder Pal

5

Un buon esempio di schema strategico sarebbe in un gioco in cui possiamo avere personaggi diversi e ogni personaggio può avere più armi per attaccare ma alla volta può usare solo un'arma. Quindi abbiamo il personaggio come contesto, ad esempio Re, Comandante, Cavaliere, Soldato e arma come strategia in cui attack () potrebbe essere il metodo / algoritmo che dipende dalle armi utilizzate. Quindi se le classi di armi concrete fossero Sword, Axe, Crossbow, BowAndArrow ecc. Implementerebbero tutte il metodo attack (). Sono sicuro che non sono necessarie ulteriori spiegazioni.


1
Pensavo che la risposta accettata dovesse parlare di questo esempio :)
Jeancarlo Fontalvo

2

Ho usato l'approccio strategico in un motore abbastanza complesso in un'applicazione che è un buon esempio. Essenzialmente il ruolo del motore era quello di andare a trovare prima un elenco di persone che avevano un widget, il secondo ruolo era quello di capire quali erano le 10 persone migliori con un widget in base a un numero sconosciuto di parametri (cose come la distanza dei prezzi precedenti affari insieme , ammontare in magazzino, opzioni di spedizione ecc ecc ecc ...)

In sostanza, ciò che abbiamo fatto è stato suddividere il problema in due strategie, la prima è il recupero dei dati, poiché sapevamo di avere più fonti dei nostri widget e dovevamo essere in grado di ottenere i dati e trasformarli in una struttura comune.

Poi ci siamo anche resi conto che avevamo più algoritmi, alcuni basati sulla ponderazione dei parametri, altri erano molto strani e propizi e non potevo rendere loro giustizia senza tirare fuori visioni e grafici e beh si ottiene l'immagine, avevamo molti algoritmi per selezionando le persone migliori.

Il nostro stesso servizio era molto essenziale, definiva essenzialmente gli input, gli output e faceva una certa normalizzazione dei dati, inoltre utilizzava un modello di provider per collegare i fornitori di dati specifici dell'applicazione e i fornitori di algoritmi che utilizzavano la strategia. Questo era un sistema abbastanza efficace.

Abbiamo discusso se stessimo usando una strategia o un modello di modello che non abbiamo mai risolto.


2

Sei sicuro che lo stato di un "ordine" non sia un modello di stato? Ho la sensazione che un ordine non verrà gestito in modo diverso a seconda del suo stato.

Prendiamo ad esempio il metodo Ship on the Order:

order.Ship();
  • Se il metodo di spedizione varia in funzione del suo stato, allora hai uno schema strategico.
  • Se tuttavia il metodo Ship () ha successo solo quando l'ordine è stato pagato e l'ordine non è stato ancora spedito, hai uno schema di stato.

Il miglior esempio del modello di stato (e altri modelli) che ho trovato è stato nel libro " Head First Design Patterns ", che è sorprendente. Un secondo vicino sarà la serie di modelli di blog di David Cumps .


2

Supponiamo che tu voglia scrivere un algoritmo per calcolare l' ennesimo Xday di un dato mese e anno, ad esempio, il secondo lunedì di ottobre 2014. Vuoi usare la classe Time di Android android.text.format.Timeper rappresentare la data, ma vuoi anche scrivere un algoritmo generico che può essere applicato anche a java.util.Calendar.

Questo è quello che ho fatto.

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, la classe con l'algoritmo generico:

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

Nella mia app Android, chiamerei qualcosa di simile

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

Se voglio riutilizzare lo stesso algoritmo per java.util.Calendar, scriverei semplicemente una classe CalendarMath che implementa i tre metodi in DatetimeMath e quindi utilizzare

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

Qualche settimana fa, ho aggiunto un'interfaccia Java comune che è stata implementata da uno dei nostri oggetti di dominio. Questo oggetto di dominio è stato caricato dal database e la rappresentazione del database era uno schema a stella con circa 10+ rami. Una delle conseguenze di avere un oggetto di dominio così pesante è che abbiamo dovuto creare altri oggetti di dominio che rappresentassero lo stesso schema, anche se meno pesante. Quindi ho fatto in modo che gli altri oggetti leggeri implementassero la stessa interfaccia. In altre parole, avevamo:

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

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

Inizialmente, volevo usare CollectibleElephantper ordinare Elephanti messaggi di posta elettronica . Abbastanza rapidamente, i miei compagni di squadra si sono lanciati CollectibleElephantper eseguire controlli di sicurezza, filtrarli quando venivano inviati alla GUI, ecc.


1

Abbiamo dovuto creare un'interfaccia di provisioning di terze parti per una piattaforma aziendale con un database molto complicato. L'invio dei dati da sottoporre a provisioning era un elenco dei nostri tipi di dati che erano stati inseriti in una coda di priorità nella nostra applicazione in modo che potessero essere scritti nel database nell'ordine corretto a causa delle dipendenze.

Il processo per scrivere quei dati è stato quindi abbastanza semplice, continua a spuntare dalla parte superiore della coda di priorità e quindi scegli una strategia in base al tipo di oggetto che estrai.


0

Da wikipedia

Nella programmazione di computer, il modello di strategia (noto anche come modello di politica) è un modello di progettazione di software comportamentale che consente di selezionare un algoritmo in fase di esecuzione. Invece di implementare direttamente un singolo algoritmo, il codice riceve istruzioni in fase di esecuzione su quale in una famiglia di algoritmi utilizzare

Nell'applicazione Windows Paint puoi vedere un modello di strategia in cui puoi scegliere la forma e il colore in modo indipendente in sezioni diverse. Qui la forma e il colore sono gli algoritmi che possono essere modificati in fase di esecuzione.

Se desideri disegnare un cerchio con il colore rosso, invece di fornire un'opzione di "RedCircle", ti consentono di scegliere il cerchio e un colore a tua scelta.

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

Senza il modello di strategia aumenterà il numero di classi con il prodotto cartesiano di forma e colore. Anche l'interfaccia cambia per ogni implementazione.


0

Immagina ad esempio un gioco sparatutto con nemici IA. Vuoi che combattano continuamente in modi diversi in base a ciò che accade. Con il pattern strategico puoi continuamente ripetere e cambiare dinamicamente il modo in cui una specifica azione verrà eseguita.

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());
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.