Uso prático do modelo de estratégia

O uso de modelos (ou padrões) na programação orientada a objetos deriva do desejo de tornar o código mais simples, mais confiável e não reinventar a roda, além de organizar efetivamente o trabalho conjunto dos programadores com diferentes níveis de treinamento, dando-lhes uma base única para entender o código-fonte na estrutura conceitual da lógica de negócios. formulários. Isso significa que aprender padrões de design é uma etapa fundamental no desenvolvimento profissional de um programador.

Como estudar padrões de design? Existem duas abordagens: chata e inteligível (Você gosta da minha classificação?). Uma abordagem chata envolve o estudo acadêmico de uma lista de padrões usando exemplos abstratos. Pessoalmente, prefiro o oposto - uma abordagem inteligível, ao definir a tarefa em um nível relativamente alto de formulação, permite escolher padrões de design. Embora você possa combinar as duas abordagens.

Então vamos?

O modelo de estratégia refere-se a um grupo de modelos comportamentais.

Breve definição do modelo de estratégia


O modelo serve para alternar entre uma família de algoritmos quando um objeto altera seu comportamento com base em uma alteração em seu estado interno.

Exemplos práticos de aplicação do modelo de estratégia


  • Classificação: queremos classificar esses números, mas não sabemos se usaremos o BrickSort, o BubbleSort ou qualquer outra classificação. Por exemplo, você tem um site no qual uma página exibe elementos com base na popularidade. No entanto, muitas coisas podem ser "Populares" (maioria das visualizações, mais inscritos, data de criação, maior atividade, menos comentários). Caso o gerenciamento ainda não saiba exatamente como fazer um pedido e queira experimentar diferentes pedidos posteriormente, crie uma interface (IOrderAlgorithm ou outra coisa) com o método order e permita que o objeto Orderer delegue a ordem da implementação concreta da interface IOrderAlgorithm . Você pode criar CommentOrderer, ActivityOrderer, etc., e simplesmente desativá-los quando novos requisitos aparecerem.
  • Processando uma fila de objetos heterogêneos (processamento de fila e salvando dados): Um exemplo seria um sistema proxy que acumule objetos de diferentes fontes de dados; depois de extrair um objeto da fila e salvá-lo, é determinado pela estratégia de seleção com base nas propriedades desse objeto.
  • Validação Precisamos verificar os elementos de acordo com a "Alguma regra", mas ainda não está claro qual será essa regra e podemos pensar em novas.
  • Autenticação: selecione uma estratégia de autenticação entre os esquemas Basic, Digest, OpenID, OAuth.

Aqui está um exemplo:

interface AuthStrategy {
    auth(): void;
}
class Auth0 implements AuthStrategy {
    auth() {
        log('Authenticating using Auth0 Strategy')
    }
}
class Basic implements AuthStrategy {
    auth() {
        log('Authenticating using Basic Strategy')
    }
}
class OpenID implements AuthStrategy {
    auth() {
        log('Authenticating using OpenID Strategy')
    }
}

class AuthProgram {
    private _strategy: AuthStrategy
    use(strategy: AuthStrategy) {
        this._strategy = strategy
        return this
    }
    authenticate() {
        if(this._strategy == null) {
            log("No Authentication Strategy set.")
        }
        this._strategy.auth()
    }
    route(path: string, strategy: AuthStrategy) {
        this._strategy = strategy
        this.authenticate()
        return this
    }
}

  • (games): — , , , , , , , . , , , , . «», «», «» Attack() . , «», «», «», « » Attack ().
  • (storing information): , , -. , PDF, , . , ; , , A, B C, . PDF , / PDF. , , , , , , B C, , A. , . PDF, , , . . Dependency Injection « / » ( , ), , , , , . , ( , ), , , , . « », «cleanUp» , , , , .
  • (outputting): X , CSV, XML, JSON .
  • (invoicing): - , , - .
  • (navigation): .
  • (logging): Log4Net Log4j , Appenders, Layouts, and Filters.
  • Criptografia: para arquivos pequenos, você pode usar a estratégia na memória quando o arquivo inteiro for lido e armazenado na memória (por exemplo, para arquivos <1 GB). Para arquivos grandes, você pode usar uma estratégia diferente, na qual partes do arquivo são lidas na memória e os resultados parcialmente criptografados são armazenados nos arquivos tmp. Essas podem ser duas estratégias diferentes para a mesma tarefa.

Aqui está um exemplo:

//   ""
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             //   byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             //     .... 
         }

}

//   
File file = getFile();
Cipher c = CipherFactory.getCipher( file.size());
c.performAction();

  • Editor gráfico: por exemplo, no aplicativo Windows Paint, há uma implementação de um modelo de estratégia no qual você pode selecionar independentemente a forma e a cor em diferentes seções. Aqui, forma e cor são algoritmos que podem ser alterados em tempo de execução.

Shape redCircle = new RedCircle(); //    «»
Shaped redCircle = new Shape("red","circle"); //   «»

SÓLIDO e implementação do modelo de estratégia


Qual é o principal problema que o modelo "Estratégia" resolve? Na verdade, este é um substituto para o código plano IF .... QUE ... ... na sua implementação de objetos.

Exemplo de código plano sujo (incorreto):

class Document {...}
class Printer {
    print(doc: Document, printStyle: Number) {
        if(printStyle == 0 /*   */) {
            // ...
        }
        if(printStyle == 1 /*  */) {
            // ...            
        }
        if(printStyle == 2 /*     */) {
            // ...
        }
        if(printStyle == 3 /*     */) {
            // ...            
        }
        if(printStyle == 4 /*     */) {
            // ...
        }
        // ...
    }
}

Um exemplo do mesmo código com o modelo "Estratégia" (corretamente):

class Document {...}
interface PrintingStrategy {
    printStrategy(d: Document): void;
}
class ColorPrintingStrategy implements PrintingStrategy {
    printStrategy(doc: Document) {
        log(" ")
        // ...
    }
}
class InvertedColorPrintingStrategy implements PrintingStrategy {
    printStrategy(doc: Document) {
        log("  ")
        // ...
    }
}
class Printer {
    private printingStrategy: PrintingStrategy
    print(doc: Document) {
        this.printingStrategy.printStrategy(doc)
    }
}

Aqui está outro exemplo da implementação correta do modelo de estratégia baseado no SOLID.

//  /
interface LockOpenStrategy {
    open();
    lock();
}
//      
class RetinaScannerLockOpenStrategy implements LockOpenStrategy {
    open() {
        //...
    }
    lock() {
        //...
    }
}

//       
class KeypadLockOpenStrategy implements LockOpenStrategy {
    open() {
        if(password != ""){
            log("Entry Denied")
            return
        }
        //...
    }
    lock() {
        //...
    }
}
//        .
abstract class Door {
    public lockOpenStrategy: LockOpenStrategy
}
//   .
class GlassDoor extends Door {}
//    .
class MetalDoor extends Door {}
//       .
class DoorAdapter {
    openDoor(d: Door) {
        d.lockOpenStrategy.open()
    }
}

Abaixo está a codificação real da lógica.

var glassDoor = new GlassDoor(); //   
glassDoor.lockOpenStrategy = new RetinaScannerLockOpenStrategy(); //         
var metalDoor = new MetalDoor(); //     
metalDoor.lockOpenStrategy = new KeypadLockOpenStrategy(); //      .
var door1 = new DoorAdapter().openDoor(glassDoor); //    . 
var door2  = new DoorAdapter().openDoor(metalDoor); //    . 

Como você pode ver acima, este é um código totalmente orientado a objetos que exclui o estilo de procedimento IF ... ELSE ....... ou SWITCH ... CASO ...

A propósito, por que usamos a classe do adaptador? A porta em si não abre, sempre abre com algo de um lado e, por outro lado, pode haver alguns eventos anteriores à abertura da porta ou após a conclusão de sua abertura, por exemplo, BeforeOpen () e AfterOpen (), também podem ser vinculados a adaptador.

Refatoração e modelo de estratégia


O modelo "Estratégia" deve ser usado quando você começar a perceber algoritmos repetidos, mas em variações diferentes. Portanto, é necessário dividir os algoritmos em classes e preenchê-los conforme necessário em seu programa.

Além disso, se você observar instruções condicionais duplicadas em torno de um algoritmo irmão.
Quando a maioria das classes tem comportamento associado. E então você precisa selecioná-lo e movê-los para classes separadas.

Espero que essa seleção de exemplos ajude você a usar o modelo de estratégia adequadamente.
Ficarei feliz se você puder dar mais exemplos desse modelo nos comentários.

Feliz codificação, amigos e colegas!

Source: https://habr.com/ru/post/undefined/


All Articles