Design Patterns (java) - Estratégia com campos. Sempre aceitável?

5

Tanto aqui no estouro de pilha quanto no Java Effective, sugere-se que os padrões de design de estratégia sejam sem estado. De fato, no livro, também é sugerido que cada objeto de estratégia seja um singleton.

O problema que tenho é que algumas estratégias que eu viso para o meu programa precisam de estados / campos. Ou porque eles são dependentes do caminho em seu comportamento ou porque eu os quero heterogêneos (uma distribuição estatística de estratégias similares, se você preferir).
Isso me força a quebrar as duas sugestões Java: Eu instancio uma nova estratégia para cada classe de usuário E cada uma dessas estratégias contém seus próprios campos.
Isso é muito ruim? Deveria ser feito de forma diferente?

Foi-me sugerido manter os campos que tornam a estratégia heterogênea na classe que a utiliza e depois passá-la como argumento. Eu acho isso muito anti-oo. Esses campos não pertencem à classe de usuário. Na verdade, se essa classe usar outra estratégia, talvez não precise desses campos. Isso parece correr contra o motivo pelo qual estou usando o padrão de estratégia em primeiro lugar.
Principalmente estou muito confuso

Eu faço um exemplo simples aqui. Imagine que você tenha uma classe Gambler, que representa alguém fazendo apostas em cavalos. Agora, essa classe exigirá uma estratégia predictStrategy que funcionará assim:

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

Agora, posso ter muitas implementações em que a estratégia é escolher aleatoriamente ou escolher o cavalo branco ou o que for. Isso é fácil.
Imagine, porém, que eu implemente uma estratégia que analise previsões passadas e "aprenda" um pouco com seus erros do passado. Claramente, cada estratégia terá que ter sua própria memória para aprender. Eu poderia ter que adicionar mais um método para a interface (ou fazer uma extensão)

interface predictStrategy{
    public Horse predictWinningHorse(HorseRace r);

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

Para que a classe Gambler chame "strategy.addObservation (...)" no final de cada corrida para melhorar seu poder preditivo.
Isso pode ser realmente feito com um objeto de estratégia stateless ? Parece impossível para mim.

    
por CarrKnight 27.07.2012 / 03:53
fonte

2 respostas

2

Eu diria que cada instância deve permanecer sem estado, uma vez que é construído, ou seja, não deve manter estado extra através de invocações que muda com base nos parâmetros. Um exemplo clássico é uma calculadora de impostos.

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

Embora FixedPercentOfSubtotal tenha um membro (estado), ele é fornecido no momento da construção e nunca é alterado. Você poderia armazenar uma instância por estado, tornando-as quase singletons.

Atualizar

Nem o artigo da Wikipedia nem este Strategy Pattern faz qualquer estipulação de que as implementações não devem manter o estado entre as chamadas. É menos comum porque as estratégias são projetadas para serem intercambiáveis e conectáveis em tempo de execução, mas eu não descartaria isso.

No entanto, você precisava adicionar um novo método para permitir que a implementação de uma estratégia seja um sinalizador vermelho. Outras implementações podem não precisar disso. Você vai defini-lo na interface de qualquer maneira? Pode fazer mais sentido ter a estratégia de rastreamento histórico implementando RaceListener e adicioná-la à instância da pista de corrida. Isso permitiria que ele fosse compartilhado entre vários usuários como um singleton.

    
por 27.07.2012 / 04:09
fonte
1

No seu exemplo da estratégia de previsão de cavalos, você tem uma estratégia mista e análise de histórico.

A solução é gravar o histórico do cavalo em outro lugar, e simplesmente substituir a estratégia existente por uma criada a partir de diferentes parâmetros calculados (por outra, classe de fábrica) de desempenho passado.

Dessa forma, você tem três classes distintas:

  • Estratégia (sem estado e imutável)
  • Coletor de histórico de previsão
  • Fábrica de estratégia (que analisa o histórico para ajustar os parâmetros usados para criar uma nova estratégia)

Cada um com suas próprias preocupações independentes e não relacionadas. Você pode escolher com que frequência trocar a instância da estratégia por uma nova (espero que melhor).

    
por 27.07.2012 / 05:19
fonte