Exemplo do mundo real do padrão de estratégia

94

Tenho lido sobre o principal OCP e como usar o padrão de estratégia para fazer isso.

Eu ia tentar explicar isso para algumas pessoas, mas o único exemplo em que consigo pensar é usar diferentes classes de validação com base no status de um "pedido".

Eu li alguns artigos online, mas eles geralmente não descrevem um motivo real para usar a estratégia, como gerar relatórios / contas / validação etc ...

Existem exemplos do mundo real onde você acha que um padrão de estratégia é comum?

Ravindra babu
fonte

Respostas:

99

Que tal isso:

Você tem que criptografar um arquivo.

Para arquivos pequenos, você pode usar a estratégia "na memória", onde o arquivo completo é lido e mantido na memória (digamos para arquivos <1 gb)

Para arquivos grandes, você pode usar outra estratégia, onde partes do arquivo são lidas na memória e os resultados criptografados parciais são armazenados em arquivos tmp.

Essas podem ser duas estratégias diferentes para a mesma tarefa.

O código do cliente seria o mesmo:

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

}

o

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

Retornaria a instância de estratégia correta para a cifra.

Eu espero que isso ajude.

(Eu nem sei se Cipher é a palavra certa: P)

OscarRyz
fonte
8
O seu exemplo não é mais um padrão de fábrica? Também acho que não vai funcionar em C # por exemplo. Seu método "getCipher ()" é um método estático, mas em C # você não pode definir um método estático em uma interface (nem em Java, eu acho, mas não tenho certeza).
FrenchData
10
Eles vão juntos. O Factory cria a estratégia, mas a própria estratégia mantém o algoritmo para realizar a (basicamente) mesma operação. A estratégia também pode ser alterada em tempo de execução. Sobre o método de fábrica você está correto, eu mudei.
OscarRyz
Para adicionar o ponto Osacars, sem fábrica isso pode ser criado sem fábrica Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder
Concordo com @FrenchData. Embora seja um ótimo exemplo, a presença de CipherFactorypode confundir aqueles que não estão familiarizados com o padrão Strategy.
user487772
1
O padrão de fábrica tem a ver com a criação, a estratégia tem a ver com o comportamento. Existe uma ligeira diferença, certo?
nhoxbypass
61

Novamente, uma postagem antiga, mas ainda aparece em pesquisas, então adicionarei mais dois exemplos (o código está em C #). Eu adoro o padrão de estratégia, pois ele me salvou muitas vezes quando os gerentes de projeto dizem: "Queremos que o aplicativo faça 'X', mas 'X' ainda não está claro e pode mudar em um futuro próximo. " Este vídeo explicando o padrão de estratégia usa o StarCraft como exemplo.

Coisas que se enquadram nesta categoria:

  • Classificação: queremos classificar esses números, mas não sabemos se vamos usar BrickSort, BubbleSort ou alguma outra classificação

  • Validação: precisamos verificar os itens de acordo com "Alguma regra", mas ainda não está claro qual será essa regra e podemos pensar em novas.

  • Jogos: queremos que o jogador caminhe ou corra ao se mover, mas talvez no futuro ele também seja capaz de nadar, voar, se teletransportar, cavar no subsolo, etc.

  • Armazenando informações: Queremos que o aplicativo armazene informações no Banco de Dados, mas posteriormente pode ser necessário salvar um arquivo ou fazer um webcall

  • Saída: precisamos gerar X como uma string simples, mas posteriormente pode ser um CSV, XML, JSON, etc.


Exemplos

Eu tenho um projeto onde os usuários podem atribuir produtos a pessoas em um banco de dados. Esta atribuição de um produto a uma pessoa tem um status que é "Aprovado" ou "Recusado", que depende de algumas regras de negócios. Por exemplo: se um usuário atribui um produto a uma pessoa com uma certa idade, seu status deve ser recusado; Se a diferença entre dois campos no item for maior que 50, seu status será recusado etc.

Agora, no momento do desenvolvimento, essas regras de negócios ainda não estão totalmente claras e novas regras podem surgir a qualquer momento. O poder do stragety-pattern é que fiz um RuleAgent, que recebe uma lista de IRules.

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

No momento de atribuir um produto a uma pessoa, eu crio um RuleAgent, forneço a ele uma lista de regras (que implementam IRule) e peço para validar uma atribuição. Ele vai passar por todas as suas regras. Que, como todos implementam a mesma interface, todos possuem o IsApprovedmétodo e retornam falso se algum deles retornar falso.

Agora, por exemplo, quando o gerente aparece de repente e diz, também precisamos recusar todas as atribuições de estagiários, ou todas as atribuições de pessoas trabalhando horas extras ... Você cria novas aulas como esta:

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

Você vê que não precisa continuar adicionando ou removendo instruções ou código if, apenas crie uma nova classe de regra que implemente a interface IRUle e troque-a quando necessário.


Outro ótimo exemplo: a série de vídeos de Scott Allen em http://www.asp.net/mvc/pluralsight, onde ele usa o padrão de estratégia na parte do teste de unidade do aplicativo

Ele constrói um site que possui uma página que exibe itens com base na popularidade. No entanto, "Popular" pode ser muitas coisas (a maioria das visualizações, a maioria dos assinantes, a data de criação, a maioria das atividades, a menor quantidade de comentários, etc.) e, no caso de o gerenciamento ainda não saber exatamente como fazer o pedido, pode querer experimentar diferentes pedidos em uma data posterior. Você faz uma interface (IOrderAlgorithm ou algo assim) com um método de pedido e deixa um objeto Orderer delegar o pedido para uma implementação concreta da interface IOrderAlgorithm. Você pode fazer um "CommentOrderer", "ActivityOrderer", etc ... E apenas substituí-los quando surgirem novos requisitos.

Céryl Wiltink
fonte
Eu sei que isso está um pouco fora do escopo da pergunta, mas o que vem a seguir? Temos isso InternRuleagora, mas como estamos acionando OvertimeRule? Como podemos garantir que qualquer lógica chamada OvertimeRule.IsApprovedagora também chame InternRule.IsApproved?
Spencer Ruport
13

Notas principais:

  1. Estratégia é um padrão de design comportamental. É usado para alternar entre famílias de algoritmos.

  2. Este padrão contém uma interface de estratégia abstrata e muitas implementações de estratégia concretas ( algoritmos ) dessa interface.

  3. O aplicativo usa apenas interface de estratégia . Dependendo de algum parâmetro de configuração, a estratégia concreta será marcada para a interface .

Diagrama UML da wikipedia

insira a descrição da imagem aqui

Um exemplo real: companhias aéreas oferecendo descontos durante alguns meses (julho-dezembro) . Você pode ter um módulo de tarifa , que decide as opções de preço dependendo do número do mês.

Dê uma olhada em um exemplo simples. Este exemplo pode ser estendido para aplicativos de varejo on-line, que oferecem descontos para itens do carrinho de compras em dias especiais / happy hours facilmente.

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

}

resultado:

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

Artigos úteis:

padrão de estratégia por dzone

padrão de estratégia por sourcemaking

Ravindra babu
fonte
12

Posso pensar em vários exemplos bastante simples:

  • Classificando uma lista. A estratégia é a comparação usada para decidir qual dos dois itens da lista é o "Primeiro"
  • Você pode ter um aplicativo onde o próprio algoritmo de classificação (QuickSort, HeapSort, etc.) pode ser escolhido em tempo de execução
  • Anexos, layouts e filtros em Log4Net e Log4j
  • Gerenciadores de layout em kits de ferramentas de IU
  • Compressão de dados. Você pode ter uma interface ICompressor cujo único método se parece com isto:

    byte [] comprimir (byte [] entrada);

    Suas classes de compressão concretas podem ser coisas como RunLengthCompression, DeflateCompression, etc.

Eric Pohl
fonte
9

Um uso comum do padrão de estratégia é definir estratégias de classificação customizadas (em linguagens sem funções de ordem superior), por exemplo, classificar uma lista de strings por comprimento em Java, passando uma classe interna anônima (uma implementação da interface de estratégia):

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

De maneira semelhante, estratégias podem ser usadas para consultas nativas com bancos de dados de objetos, por exemplo, em db4o:

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

Eu tenho um aplicativo que sincroniza sua base de usuários a cada dia com o nosso diretório corporativo. Os usuários são elegíveis ou não com base em seu status na Universidade. A cada dia, o programa de provisionamento passa e garante que aqueles que deveriam ser elegíveis sejam provisionados no aplicativo e aqueles que não são desprovisionados (na verdade, de acordo com um algoritmo de degradação normal, mas isso não vem ao caso). No sábado, faço uma atualização mais completa que sincroniza algumas propriedades de cada usuário, além de garantir que eles tenham a elegibilidade adequada. No final do mês, faço algum processamento da fatura com base no uso naquele mês.

Eu uso um padrão de estratégia combinável para fazer essa sincronização. O programa principal basicamente escolhe uma estratégia mestre dependendo do dia da semana (sincronizar mudanças apenas / sincronizar tudo) e a época do semestre em relação ao calendário acadêmico. Se o ciclo de faturamento está terminando, ele também o compõe com uma estratégia de faturamento. Em seguida, executa a estratégia escolhida por meio de uma interface padrão.

Não sei o quão comum isso é, mas senti que era um ajuste perfeito para o padrão de estratégia.

Tvanfosson
fonte
Este é um exemplo muito bom. Além disso, ele mostra claramente a diferença entre o padrão de comando e estratégia em poucas palavras - a intenção. "O programa principal basicamente escolhe uma estratégia mestre dependendo do dia da semana"
Utsav T
7

Sei que essa é uma pergunta antiga, mas acho que tenho outro exemplo interessante que implementei recentemente.

Este é um exemplo muito prático do padrão de estratégia usado em um sistema de entrega de documentos.

Eu tinha um sistema de entrega de PDF que recebia um arquivo contendo muitos documentos e alguns metadados. Com base nos metadados, ele decidiu onde colocar o documento; digamos, dependendo dos dados, eu poderia armazenar o documento em A, BouC sistemas de armazenamento, ou uma combinação dos três.

Diferentes clientes usavam este sistema, e eles tinham diferentes requisitos de reversão / tratamento de erros em caso de erros: um queria que o sistema de entrega parasse no primeiro erro, deixasse todos os documentos já entregues em seus depósitos, mas parasse o processo e não entregasse mais nada ; outro queria retroceder Bem caso de erros ao armazenar C, mas deixar o que já foi entregue A. É fácil imaginar que um terceiro ou quarto também terá necessidades diferentes.

Para resolver o problema, criei uma classe de entrega básica que contém a lógica de entrega, além de métodos para reverter coisas de todos os armazenamentos. Esses métodos não são chamados diretamente pelo sistema de entrega em caso de erros. Em vez disso, a classe usa injeção de dependência para receber uma classe "Estratégia de manipulação de rollback / erro" (com base no cliente que usa o sistema), que é chamada em caso de erros, que por sua vez chama os métodos de rollback se for apropriado para essa estratégia.

A própria classe de entrega relata o que está acontecendo para a classe de estratégia (quais documentos foram entregues a quais armazenamentos e quais falhas aconteceram) e, sempre que ocorre um erro, ela pergunta à estratégia se deve continuar ou não. Se a estratégia diz "pare", a classe chama o método "cleanUp" da estratégia, que usa as informações relatadas anteriormente para decidir quais métodos de rollback chamar da classe de entrega ou simplesmente não faz nada.

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

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

Portanto, agora tenho duas estratégias diferentes: uma é a QuitterStrategy(que fecha no primeiro erro e não limpa nada) e a outra é a MaximizeDeliveryToAStrategy(que tenta, tanto quanto possível, não abortar o processo e nunca reverter o material entregue ao armazenamento A, mas reverte coisas de Bse a entrega Cfalhar).

Do meu entendimento, este é um exemplo do padrão de estratégia. Se você (sim, você está lendo) acha que estou errado, comente abaixo e me avise. Estou curioso para saber o que constituiria um uso "puro" do padrão de estratégia e quais aspectos da minha implementação violam a definição. Acho que parece um pouco engraçado porque a interface de estratégia é um pouco gorda. Todos os exemplos que vi até agora usam apenas um método, mas ainda acho que isso encapsula um algoritmo (se uma parte da lógica de negócios pode ser considerada um algoritmo, o que eu acho que é).

Como a estratégia também é notificada sobre eventos durante a execução da entrega, ela também pode ser considerada um Observador , mas isso é outra história.

Com um pouco de pesquisa, parece que este é um "padrão composto" (como MVC, um padrão que usa vários padrões de design de uma maneira particular) chamado de Consultor . É um consultor sobre se a entrega deve continuar ou não, mas também é um gerenciador de erros ativo, pois pode reverter coisas quando solicitado.

De qualquer forma, este é um exemplo bastante complexo que pode fazer com que você sinta que os usos do padrão de estratégia são muito simples / bobos. Pode ser muito complexo e ainda mais aplicável quando usado junto com outros padrões.

Gui Prá
fonte
6

O padrão de estratégia é o padrão mais comumente usado, especialmente para validações e algoritmos de classificação.

Deixe-me explicar com um exemplo prático simples

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

}  

O código de teste para isso é

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

}

O mesmo exemplo foi tirado de http://coder2design.com/strategy-pattern/

Jatinder Pal
fonte
Diferentes usos do padrão de estratégia: Validações: Quando há muitas validações precisam ser feitas em seu código. Algoritmos diferentes: especialmente quando diferentes algoritmos de classificação podem ser usados, por exemplo, classificação por bolha ou classificação rápida. Armazenamento de informações: quando podemos obter informações em locais diferentes, por exemplo, banco de dados ou sistema de arquivos. Análise: durante a análise, podemos usar estratégias diferentes para entradas diferentes. Estratégias de filtragem. Estratégias de layout.
Jatinder Pal
5

Um bom exemplo de padrão de estratégia seria em um jogo onde podemos ter personagens diferentes e cada personagem pode ter várias armas para atacar, mas ao mesmo tempo pode usar apenas uma arma. Portanto, temos o personagem como contexto, por exemplo Rei, Comandante, Cavaleiro, Soldado e arma como estratégia onde ataque () pode ser o método / algoritmo que depende das armas utilizadas. Portanto, se as classes de armas de concreto fossem Espada, Machado, Besta, BowAndArrow etc., todas implementariam o método attack (). Estou certo de que não são necessárias mais explicações.

Sandipan Karmakar
fonte
1
Achei que a resposta aceita fosse falar sobre este exemplo :)
Jeancarlo Fontalvo
2

Usei a abordagem de estratégia em um mecanismo bastante complexo em um aplicativo que é um bom exemplo. Essencialmente, a função do motor era ir e primeiro encontrar uma lista de pessoas que tinham um widget, sua segunda função era descobrir quais eram as 10 melhores pessoas com um widget com base em um número desconhecido de parâmetros (coisas como preço, distância, negócios anteriores juntos , quantidade em estoque, opções de envio etc etc etc ...)

Essencialmente, o que fizemos foi dividir o problema em duas estratégias, a primeira sendo a recuperação de dados, pois sabíamos que tínhamos várias fontes de nossos widgets e precisávamos ser capazes de obter os dados e transformá-los em uma estrutura comum.

Então, também percebemos que tínhamos vários algoritmos, alguns eram baseados na ponderação dos parâmetros, outros eram muito estranhos e adequados e eu não poderia fazer justiça a eles sem apresentar visios e gráficos e bem, você entendeu, tínhamos muitos algoritmos para selecionando as melhores pessoas.

Nosso serviço em si era exatamente o que essencialmente definia as entradas, saídas e fazia alguma normalização dos dados, também usava um padrão de provedor para conectar os provedores de dados específicos do aplicativo e os provedores de algoritmo que usavam a estratégia. Este foi um sistema bastante eficaz.

Tivemos alguns debates se estávamos usando uma estratégia ou um padrão de modelo que nunca resolvemos.

JoshBerke
fonte
2

Tem certeza de que o status de um "pedido" não é um padrão de estado? Tenho um palpite de que um pedido não será tratado de forma diferente dependendo do seu status.

Tomemos por exemplo o método Enviar no Pedido:

order.Ship();
  • Se o método de envio varia em função de seu status, então você tem um padrão de estratégia.
  • Se, no entanto, o método Ship () for bem-sucedido apenas quando o pedido for pago e o pedido ainda não tiver sido enviado, você terá um padrão de estado.

O melhor exemplo do padrão de estado (e outros padrões) que encontrei foi no livro " Head First Design Patterns ", que é incrível. Em segundo lugar estará a série de padrões de blog de David Cumps .

Grootjans
fonte
2

Digamos que você queira escrever um algoritmo para calcular o enésimo Xdia de um determinado mês e ano, por exemplo, a segunda segunda-feira de outubro de 2014. Você deseja usar a classe Tempo do Android android.text.format.Timepara representar a data, mas também deseja escrever um algoritmo genérico que também pode se aplicar a java.util.Calendar.

Isso é o que eu fiz.

Em DatetimeMath.java:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

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

Em OrdinalDayOfWeekCalculator.java, a classe com o algoritmo genérico:

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

Em meu aplicativo Android, eu chamaria algo como

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

Se eu quiser reutilizar o mesmo algoritmo para java.util.Calendar, eu apenas escreveria uma classe CalendarMath que implementa os três métodos em DatetimeMath e então usaria

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);
anomal
fonte
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("[email protected]", "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);
    }
}
Vivek Goel
fonte
1

Algumas semanas atrás, adicionei uma interface Java comum que foi implementada por um de nossos objetos de domínio. Este objeto de domínio foi carregado do banco de dados e a representação do banco de dados era um esquema em estrela com cerca de 10+ ramificações. Uma das consequências de ter um objeto de domínio de peso tão pesado é que tivemos que fazer outros objetos de domínio que representavam o mesmo esquema, embora menos pesado. Então, fiz os outros objetos leves implementarem a mesma interface. Dito de outra forma, tínhamos:

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

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

Originalmente, eu queria usar CollectibleElephantpara classificar Elephants. Rapidamente, meus colegas de equipe começaram CollectibleElephanta executar verificações de segurança, filtrá-los à medida que eram enviados para a GUI etc.

Alan
fonte
1

Tivemos que criar uma interface de provisionamento de terceiros para uma plataforma corporativa com um banco de dados muito complicado. O envio de dados a serem provisionados foi como uma lista de nossos tipos de dados que foram colocados em uma fila de prioridade em nosso aplicativo para que pudessem ser gravados no banco de dados na ordem correta devido às dependências.

O processo para gravar esses dados era então bastante simples, continue aparecendo no topo da fila de prioridade e, em seguida, escolha uma estratégia com base no tipo de objeto que você extrai.

Coxy
fonte
0

Da wikipedia

Na programação de computadores, o padrão de estratégia (também conhecido como padrão de política) é um padrão de projeto de software comportamental que permite selecionar um algoritmo em tempo de execução. Em vez de implementar um único algoritmo diretamente, o código recebe instruções em tempo de execução sobre quais algoritmos em uma família de

No aplicativo do Windows Paint, você pode ver um padrão de estratégia em que pode escolher a forma e a cor independentemente em diferentes seções. Aqui, a forma e a cor são os algoritmos que podem ser alterados em tempo de execução.

Se você deseja desenhar um círculo com a cor vermelha, em vez de fornecer a opção 'RedCircle', eles permitem que você escolha o círculo e uma cor de sua escolha.

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

Sem estratégia padrão aumentará o número de classes com o produto cartesiano de forma e cor. Além disso, a interface muda para cada implementação.

bharanitharan
fonte
0

Imagine um jogo de tiro com inimigos de IA, por exemplo. Você quer que eles lutem continuamente de maneiras diferentes com base no que acontece. Com o padrão de estratégia, você pode fazer um loop contínuo e alterar dinamicamente como uma ação específica ou será executada.

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());
}
Cédric S
fonte