Design Patterns (java) - Strategia con i campi. Mai accettabile?

5

Sia qui nello stack overflow che in Java Efficace è suggerito che i pattern di progettazione della strategia dovrebbero essere apolidi. Infatti nel libro si suggerisce anche di rendere ogni oggetto strategico un singleton.

Il problema che ho è che alcune strategie che immagino per il mio programma hanno bisogno di stati / campi. O perché sono path-dependent nel loro comportamento o perché li voglio eterogenei (una distribuzione statistica di strategie simili, se preferisci).
Questo mi costringe a infrangere entrambi i suggerimenti efficaci di Java: istanzio una nuova strategia per ogni classe utente E ciascuna di queste strategie contiene i propri campi.
È molto brutto? Dovrebbe essere fatto in modo diverso?

Mi è stato suggerito di mantenere i campi che rendono la strategia eterogenea nella classe che la usa e poi passarla come argomento. Lo trovo molto anti-oo. Questi campi non appartengono alla classe utente. Infatti, se quella classe usa un'altra strategia, potrebbe non aver bisogno di quei campi. Ciò sembra andare contro la ragione per cui sto usando il modello strategico in primo luogo.
Per lo più sono solo molto confuso

Faccio un semplice esempio qui. Immagina di avere un giocatore di classe, che rappresenta qualcuno che fa scommesse sui cavalli. Ora questa classe richiederà una strategia per prevedereStrategy che funzioni in questo modo:

interface predictStrategy{
    public Horse predictWinningHorse(HorseRace r);
}

Ora, posso avere molte implementazioni in cui la strategia è scegliere a caso, o scegliere il cavallo bianco o qualsiasi altra cosa. Questo è facile.
Immaginate di implementare una strategia che guardi le previsioni del passato e in qualche modo "impari" dai suoi errori passati. Chiaramente ogni strategia dovrà avere la propria memoria su cui imparare. Potrei dover aggiungere un altro metodo all'interfaccia (o creare un'estensione)

interface predictStrategy{
    public Horse predictWinningHorse(HorseRace r);

    public void addObservation(HorseRace r, Horse oldPrediction, Horse actualWinner);
}

In modo che la classe Gambler chiami "strategy.addObservation (...)" alla fine di ogni gara per migliorare il suo potere predittivo.
Può essere veramente essere eseguito con un oggetto di strategia senza stato ? Mi sembra impossibile.

    
posta CarrKnight 27.07.2012 - 03:53
fonte

2 risposte

2

Direi che ogni istanza dovrebbe rimanere apolidia una volta costruita, cioè non dovrebbe mantenere uno stato extra tra le invocazioni che cambia in base ai parametri. Un classico esempio è un calcolatore delle imposte.

interface TaxCalculator {
    /**
     * Calculates the tax for the given purchase price and shipping charges.
     * All values are in pennies to avoid rounding.
     *
     * @param subtotal total price of all items ordered
     * @param shipping cost of shipping the order
     * @return calculated tax
     */
    int calculate(int subtotal, int shipping);
}

class NoTax implements TaxCalculator {
    public int calculate(int subtotal, int shipping) {
        return 0;
    }
}

class FixedPercentOfSubtotal implements TaxCalculator {
    private final int ratePercent;

    public FixedPercentOfSubtotal(int ratePercent) {
        this.ratePercent = ratePercent;
    }

    public int calculate(int subtotal, int shipping) {
        return subtotal * ratePercent / 100;
    }
}

Mentre FixedPercentOfSubtotal ha un membro (stato), è fornito in fase di costruzione e non cambia mai. È possibile memorizzare un'istanza per stato, rendendola quasi singletons.

Aggiorna

Né l' articolo di Wikipedia né questo Strategia modello stabilisce che le implementazioni non devono mantenere lo stato tra le chiamate. È meno comune perché le strategie sono progettate per essere intercambiabili e collegabili in fase di runtime, ma non escluderei la possibilità.

Tuttavia, era necessario aggiungere un nuovo metodo per abilitare l'implementazione di una strategia come una bandiera rossa. Altre implementazioni potrebbero non averne bisogno. Lo definirai comunque nell'interfaccia? Potrebbe essere più sensato avere la strategia di tracciamento storico implementare RaceListener e aggiungerla all'istanza della pista di gara. Ciò consentirebbe che fosse condiviso tra più utenti come un singleton.

    
risposta data 27.07.2012 - 04:09
fonte
1

Nel tuo esempio della strategia di predizione del cavallo, hai una strategia mista e analisi della storia.

La soluzione è registrare la storia del cavallo da qualche altra parte, e semplicemente sostituire la strategia esistente con una creata da diversi parametri calcolati (da un'altra classe di fabbrica) dalle prestazioni passate.

In questo modo hai tre classi distinte:

  • Strategia (senza stato e immutabile)
  • Raccoglitore della cronologia delle previsioni
  • Strategy factory (che analizza la cronologia per mettere a punto i parametri utilizzati per creare una nuova strategia)

Ciascuno con le proprie preoccupazioni indipendenti e non correlate. Puoi scegliere ogni quanto tempo scambiare l'istanza della strategia con una nuova (si spera meglio).

    
risposta data 27.07.2012 - 05:19
fonte

Leggi altre domande sui tag