Padrão de Design da Estratégia Modificado


11

Comecei a pesquisar recentemente em Design Patterns, e uma coisa que estou codificando se adequaria perfeitamente ao padrão Strategy, exceto por uma pequena diferença.

Essencialmente, alguns (mas não todos) dos meus algoritmos precisam de um ou dois parâmetros extras passados ​​para eles.

Então eu vou precisar

  • passar a eles um parâmetro extra quando eu invocar o método de cálculo

ou

  • armazená-los como variáveis ​​dentro da classe ConcreteAlgorithm e poder atualizá-los antes que eu chame o algoritmo.

Existe um padrão de design para essa necessidade / Como eu poderia implementar isso enquanto seguia o Padrão de Estratégia?

Eu considerei passar o objeto cliente para todos os algoritmos e armazenar as variáveis ​​lá, usando-o somente quando o algoritmo específico precisar dele. No entanto, acho que isso é difícil de manejar e derrota o objetivo do padrão de estratégia.

Só para esclarecer, estou implementando em Java e, portanto, não tenho o luxo de parâmetros opcionais (o que resolveria isso muito bem).


Parâmetros opcionais, como no C ++, não resolveriam nada, pois são apenas um atalho para definir vários métodos sobrecarregados.
Maaartinus 13/03/11

Eu me esforçava bastante para evitar o armazenamento de parâmetros adicionais em algum lugar em que precisei alterá-los antes do uso. Dessa forma, você torna o ConcreteAlgorithm com estado, para que não possa ser passado facilmente para outro método ou threads. Além disso, é muito fácil esquecer de definir os parâmetros.
maaartinus

Respostas:


5

Samuel, é possível encapsular o parâmetro que cada uma das estratégias leva em uma classe comum e depois estender essa classe Parameter comum para adicionar mais comportamento do que algumas de suas estratégias especificamente precisam?

Por exemplo

StrategyParameter //Base strategy parameter that most of the strategies need
        ^
        |
        |
SpecialStrategyParameter // will be used for strategies that need more parameter

E então, defina a hierarquia da estratégia como:

Interface MyStrategy {
   void myStrategyMethod(StrategyParameter parameter);
}

class MyNormalStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

chame a estratégia acima como: myNormalStrategyInstance.myStrategyMethod(strategyParameter);

class MySpecializedStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

chame a estratégia acima, passando a SpecialStrategyParameterinstância como:mySpecializedStrategy.myStrategyMethod(specialStrategyParameter);

Atualize se algo não estiver claro. Será um prazer explicar / esclarecer.


1
-1 requer downcast, quebra o encapsulamento do design. Embora seja uma melhoria no design da questão, existem maneiras melhores de esfolar esse gato.
tallseth

@allseth eu vejo downcast também. Mas não vejo maneiras melhores. Você poderia apontar uma solução melhor? Um artigo ou algo assim?
Narek

Na verdade sim. @ Jordão tem a resposta que eu preferiria, com base nos detalhes que temos na pergunta. Essa resposta corresponde aos pontos fortes do padrão de estratégia. Se seguíssemos a abordagem nesta resposta, eu gostaria de StrategyParameterconter todos os parâmetros possíveis, como um DTO. Algumas implementações da estratégia podem ignorá-las. Em alguns casos, essa é a melhor abordagem. O contexto é o rei para esse tipo de problema.
tallseth

3

Você precisa esclarecer sua estratégia .

Tudo depende de como você usa seus algoritmos. Para que sua classe cliente use implementações de estratégias diferentes de forma intercambiável, todas elas precisam ter uma abstração comum . Se eles não seguem a mesma interface, talvez você precise de abstrações diferentes .

Eu usei estratégias configuráveis antes, onde você parametriza as classes concretas na construção:

interface Strategy {
  int calculate();
}

class ConcreteStrategyThatNeedsAParameter implements Strategy {
  private final int param;
  public ConcreteStrategyThatNeedsAParameter(int param) {
    this.param = param;
  }
  public int calculate() { 
    // uses param...
  }
}

Agora, alguém ainda precisa criar uma instância dessa classe e passá-la ao seu cliente. Mas seu cliente ainda precisa apenas conhecer a Strategyinterface.

Também funciona se o seu método de estratégia usa parâmetros, mas seu cliente conhece esses parâmetros e os passa para todas as implementações com as quais trabalha.


O cliente é aquele com o contexto para fornecer o parâmetro.
andyczerwonka

1

Desde que a assinatura seja definida claramente na interface, ela ainda está em conformidade com o padrão de estratégia.

Os padrões escritos são a forma absoluta mais simples que ainda exibe o comportamento esperado, para que você possa embelezá-los desde que mantenha a intenção original. Isso, é claro, pressupõe que você queira seguir o padrão. Não faz sentido usar um padrão se ele não se encaixar, ou apenas porque está lá, mas no seu caso, acho que você está bem.


0

estendendo-se à resposta acima fornecida pelo peakit - você pode usar a abstração. Estou usando o código do peakit aqui -

Interface MyStrategy { resumo vazio myStrategyMethod (parâmetro StrategyParameter); }

classe MyNormalStrategy estende MyStrategy {substituição pública anula myStrategyMethod (parâmetro StrategyParameter) {// implementa a lógica aqui}}

classe MySpecializedStrategy estende MyStrategy {substituição pública anula myStrategyMethod (parâmetro StrategyParameter, ExtraStrategyParameter extraParameter) {// implementa a lógica aqui} }

Se entendi sua pergunta corretamente, você queria passar um parâmetro extra para certos algoritmos, certo? Informe-me se é isso que você estava procurando?


0

Se você olhar para o livro Padrões de design, não é errado, por si só, que exista algum SimpleStrategy que use menos ou nenhum dos parâmetros passados ​​ou que os parâmetros sejam um multiplicador de tamanho único / multiplicador menos comum. A escolha do design aqui é se isso prejudica você em termos de processamento extra que acaba não sendo usado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.