Notas sobre o Padrão de Estratégia

Introdução

O padrão de estratégia encapsula uma série de algoritmos em classes individuais, permitindo que cada algoritmo seja substituído facilmente. Ele permite que os algoritmos mudem independentemente dos clientes que os utilizam, também conhecido como padrão de política. Este é um padrão de comportamento de objeto.

Implementação

arquivo.h

//
// Criado por yuwp em 2024/1/12.
//

#ifndef DESIGNPATTERNS_ARQUIVO_H
#define DESIGNPATTERNS_ARQUIVO_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Estrategia {    // classe abstrata de estratégia
public:
    virtual void executar() = 0;
};

class Contexto {   // classe abstrata de estado
public:
    void definirEstrategia(Estrategia *estrategia);

    void executar();

private:
    std::shared_ptr<Estrategia> estrategiaAtual;
};

class EstrategiaConcretaX : public Estrategia {
public:
    void executar() override;
};

class EstrategiaConcretaY : public Estrategia {
public:
    void executar() override;
};


#endif //DESIGNPATTERNS_ARQUIVO_H


arquivo.cpp

//
// Criado por yuwp em 2024/1/12.
//

#include "arquivo.h"
#include <thread>
#include <unistd.h>
#include <sstream>

void Contexto::definirEstrategia(Estrategia *estrategia) {
    estrategiaAtual.reset(estrategia);
}

void Contexto::executar() {
    if (estrategiaAtual) {
        estrategiaAtual->executar();
    } else {
        std::cout << "Nenhuma estratégia definida" << std::endl;
    }
}

void EstrategiaConcretaX::executar() {
    std::cout << "Usando estratégia X" << std::endl;
}

void EstrategiaConcretaY::executar() {
    std::cout << "Usando estratégia Y" << std::endl;
}


main.cpp

#include <iostream>
#include <mutex>
#include "arquivo.h"

int main() {
    Estrategia *estrategiaX = new EstrategiaConcretaX;
    Estrategia *estrategiaY = new EstrategiaConcretaY;
    Contexto *contexto = new Contexto;
    contexto->definirEstrategia(estrategiaX);
    contexto->executar();
    contexto->definirEstrategia(estrategiaY);
    contexto->executar();

    delete contexto;

    return 0;
}

Resumo

Vantagens

  1. O padrão de estratégia oferece suporte perfeito ao princípio da abertura/fechamneto. Os usuários podem escolher algoritmos ou comportamentos sem modificar o sistema existente, além de poderem adicionar novos algoritmos de forma flexível.
  2. O padrão de estratégia fornece uma maneira de gerenciar uma família de algoritmos. A hierarquia da classe de estratégia define uma família de algoritmos ou comportamentos, e o uso apropriado da herança permite mover código comum para a classe de estratégia abstrata, evitando assim código repetido.
  3. O padrão de estratégia oferece uma alternativa à relação de herança. Sem esse padrão, a classe de ambiente que usa algoritmos poderia ter várias subclasses, cada uma fornecendo um algoritmo diferente. Isso mistura a lógica de seleção do algoritmo com a implementação do algoritmo, violando o princípio da responsabilidade única. A lógica de decisão e o algoritmo ficam entrelaçados, tornando difícil evoluí-los separadamente; além disso, a herança não permite a troca dinâmica de algoritmos durante a execução do programa.
  4. O uso do padrão de estratégia evita a necessidade de múltiplas instruções condicionais. Essas instruções são difíceis de manter, pois misturam a lógica de seleção do algoritmo com a implementação do algoritmo, codificando-a de forma rígida em uma grande estrutura condicional, sendo mais primitivo e obsoleto do que a herança.
  5. O padrão de estratégia fornece um mecanismo de reutilização de algoritmos. Como os algoritmos são extraídos e encapsulados em classes de estratégia, diferentes classes de ambiente podem reutilizar facilmente essas estratégias.

Desvantagens

  1. O cliente deve conhecer todas as classes de estratégia e escolher qual usar. Isso significa que o cliente precisa entender as diferenças entre os algoritmos para selecionar o apropriado. Em outras palavras, o padrão de estratégia só é aplicável quando o cliente conhece todos os algoritmos ou comportamentos.
  2. O padrão de estratégia pode causar a criação de muitas classes de estratégia concretas. Qualquer pequena mudança pode exigir a criação de uma nova classe de estratégia concreta.
  3. Não é possível usar múltiplas classes de estratégia ao mesmo tempo no cliente. Ou seja, durante o uso do padrão de estratégia, o cliente só pode usar uma classe de estratégia de cada vez, sem suporte para usar uma classe de estratégia para parte do funcionamento e outra classe para a parte restante.

Cenários de Aplicação

  1. Um sistema precisa escolher dinamicamente entre vários algoritmos. Esses algoritmos podem ser encapsulados em classes concretas, que são subclasses de uma classe abstrata de algoritmo. Ou seja, essas classes concretas têm uma interface uniforme. De acordo com o princípio de substituição de Liskov e a polimorfia orientada a objeto, o cliente pode escolher qualquer classe concreta de algoritmo e manter apenas um objeto do tipo classe abstrata de algoritmo.
  2. Um objeto possui muitos comportamentos, e sem um padrão apropriado, esses comportamentos teriam que ser implementados com instruções condicionais múltiplas. Nesse caso, usar o padrão de estratégia transfere esses comportamentos para as classes de estratégia concretas, evitando assim instruções condicionais múltiplas difíceis de manter.
  3. Não deseja-se que o cliente saiba sobre estruturas de dados complexas relacionadas aos algoritmos. Encapsular algoritmos e estruturas de dados relacionados nas classes de estratégia pode aumentar a confidencialidade e segurança dos algoritmos.

Exercício

arquivo.h

//
// Criado por yuwp em 2024/1/12.
//

#ifndef DESIGNPATTERNS_ARQUIVO_H
#define DESIGNPATTERNS_ARQUIVO_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Decolagem {    // classe abstrata de decolagem
public:
    virtual void iniciar() = 0; // iniciar
};

class Voo {      // classe abstrata de voo
public:
    virtual void iniciarVoo() = 0;  // iniciar voo
};

class Aeronave {
public:
    Aeronave(Decolagem *decolagem, Voo *voo);
    virtual ~Aeronave();
    virtual void iniciarDecolagem();
    virtual void iniciarVoo();
private:
    Decolagem *decolagemAtual;
    Voo *vooAtual;
};

class Simulador {   // sistema de simulação
public:
    void definirAeronave(Aeronave *aeronave);

    void iniciar();

private:
    std::shared_ptr<Aeronave> aeronaveAtual;
};

class DecolagemVertical : public Decolagem {    // estratégia de decolagem vertical
public:
    void iniciar() override;
};

class DecolagemLongaDistancia : public Decolagem {    // estratégia de decolagem longa distância
public:
    void iniciar() override;
};

class VooSubsônico : public Voo {     // estratégia de voo subsônico
public:
    void iniciarVoo() override;
};

class VooSupersônico : public Voo {     // estratégia de voo supersônico
public:
    void iniciarVoo() override;
};

class Helicóptero : public Aeronave {   // helicóptero
public:
    Helicóptero();

    void iniciarDecolagem() override;

    void iniciarVoo() override;
};

class AviaoComercial : public Aeronave {   // avião comercial
public:
    AviaoComercial();

    void iniciarDecolagem() override;

    void iniciarVoo() override;
};

class Caça : public Aeronave {   // caça
public:
    Caça();

    void iniciarDecolagem() override;

    void iniciarVoo() override;
};

class AviaoDeAtaque : public Aeronave {   // avião de ataque
public:
    AviaoDeAtaque();

    void iniciarDecolagem() override;

    void iniciarVoo() override;
};

#endif //DESIGNPATTERNS_ARQUIVO_H


arquivo.cpp

//
// Criado por yuwp em 2024/1/12.
//

#include "arquivo.h"
#include <thread>
#include <unistd.h>
#include <sstream>

Aeronave::Aeronave(Decolagem *decolagem, Voo *voo) {
    decolagemAtual = decolagem;
    vooAtual = voo;
}

Aeronave::~Aeronave() {
    if (decolagemAtual) {
        delete decolagemAtual;
    }
    if (vooAtual) {
        delete vooAtual;
    }
}

void Aeronave::iniciarDecolagem() {
    if (decolagemAtual) {
        decolagemAtual->iniciar();
    } else {
        std::cout << "Nenhuma configuração de decolagem definida" << std::endl;
    }
}

void Aeronave::iniciarVoo() {
    if (vooAtual) {
        vooAtual->iniciarVoo();
    } else {
        std::cout << "Nenhuma configuração de voo definida" << std::endl;
    }
}

void Simulador::definirAeronave(Aeronave *aeronave) {
    aeronaveAtual.reset(aeronave);
}

void Simulador::iniciar() {
    if (aeronaveAtual) {
        aeronaveAtual->iniciarDecolagem();
        aeronaveAtual->iniciarVoo();
    } else {
        std::cout << "Por favor, defina o tipo de aeronave primeiro" << std::endl;
    }
}

void DecolagemVertical::iniciar() {
    std::cout << "Decolagem vertical iniciada" << std::endl;
}

void DecolagemLongaDistancia::iniciar() {
    std::cout << "Decolagem longa distância iniciada" << std::endl;
}

void VooSubsônico::iniciarVoo() {
    std::cout << "Voo subsônico iniciado" << std::endl;
}

void VooSupersônico::iniciarVoo() {
    std::cout << "Voo supersônico iniciado" << std::endl;
}

Helicóptero::Helicóptero() : Aeronave(new DecolagemVertical(), new VooSubsônico()) {

}

void Helicóptero::iniciarDecolagem() {
    std::cout << "Helicóptero iniciando decolagem" << std::endl;
    Aeronave::iniciarDecolagem();
}

void Helicóptero::iniciarVoo() {
    std::cout << "Helicóptero iniciando voo" << std::endl;
    Aeronave::iniciarVoo();
}

AviaoComercial::AviaoComercial() : Aeronave(new DecolagemLongaDistancia(), new VooSubsônico()) {

}

void AviaoComercial::iniciarDecolagem() {
    std::cout << "Avião comercial iniciando decolagem" << std::endl;
    Aeronave::iniciarDecolagem();
}

void AviaoComercial::iniciarVoo() {
    std::cout << "Avião comercial iniciando voo" << std::endl;
    Aeronave::iniciarVoo();
}

Caça::Caça() : Aeronave(new DecolagemLongaDistancia(), new VooSupersônico()) {

}

void Caça::iniciarDecolagem() {
    std::cout << "Caça iniciando decolagem" << std::endl;
    Aeronave::iniciarDecolagem();
}

void Caça::iniciarVoo() {
    std::cout << "Caça iniciando voo" << std::endl;
    Aeronave::iniciarVoo();
}

AviaoDeAtaque::AviaoDeAtaque() : Aeronave(new DecolagemVertical(), new VooSupersônico()) {

}

void AviaoDeAtaque::iniciarDecolagem() {
    std::cout << "Avião de ataque iniciando decolagem" << std::endl;
    Aeronave::iniciarDecolagem();
}

void AviaoDeAtaque::iniciarVoo() {
    std::cout << "Avião de ataque iniciando voo" << std::endl;
    Aeronave::iniciarVoo();
}


main.cpp

#include <iostream>
#include <mutex>
#include "arquivo.h"

int main() {
    Simulador *simulador = new Simulador();
    Aeronave *aeronave = new Helicóptero();
    simulador->definirAeronave(aeronave);
    simulador->iniciar();
    std::cout << "----------------------" << std::endl;
    aeronave = new AviaoComercial();
    simulador->definirAeronave(aeronave);
    simulador->iniciar();
    std::cout << "----------------------" << std::endl;
    aeronave = new Caça();
    simulador->definirAeronave(aeronave);
    simulador->iniciar();
    std::cout << "----------------------" << std::endl;
    aeronave = new AviaoDeAtaque();
    simulador->definirAeronave(aeronave);
    simulador->iniciar();

    delete simulador;

    return 0;
}

Tags: padrão de estratégia Programação Orientada a Objetos Design de Software algoritmos dinâmicos encapsulamento de comportamento

Publicado em 10-2 02:41