Utilizando Strategy para aplicação de modelos

Strategy é um padrão de projetos do tipo comportamental que busca padronizar a comunicação entre os objetos e cria uma estratégia encadeada, sendo possível resolver tarefas maiores de maneira escalável e organizada, permitindo a utilização de múltiplos modelos encadeados ao invés de utilizarmos diversos “ifs”. Assim, podemos isolar a estratégia de aplicação nos próprios modelos, ao invés de deixar na aplicação principal.

Segundo o catálogo GOF (Gang of Four) o padrão tem como objetivo:

Definir uma família de algoritmos, encapsular cada uma delas e torná-las intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam. (https://pt.wikipedia.org/wiki/Strategy)

O que é um código fora do padrão strategy? 

Para simularmos o padrão Strategy, irei implementar os seguintes programas em Python:

  • cliente.py
  • aplica_modelo.py

O código abaixo implementa uma classe chamada Cliente, é com base nas variáveis dessa classe que escolheremos o modelo que será aplicado.

# -*- coding: UTF-8 -*-
# cliente.py
class Cliente(object):

    def __init__(self, idade, score=None, regiao=None, salario=0.0):
        self.idade = idade
        self.score = score
        self.regiao = regiao
        self.salario = salario
      
    

A classe abaixo, Aplicador_modelos, é quem guarda a lógica da aplicação do modelo.


# -*- coding: UTF-8 -*-
# Dev: Jefferson Soares
# aplica_modelo.py

class Aplicador_modelos(object):

    def calcula(self, cliente):

        # Modelo idade
        if(cliente.idade >= 30 and cliente.idade < 50):
            return 'Aplica modelo de idade'

        # Modelo salário
        elif (cliente.salario >= 5000):
            return 'Aplica modelo de salário'

        # Modelo score
        elif (cliente.score >= 500):
            return 'Aplica modelo de Score'

        else: 
            return 'Sem modelo'
        
        return modelo

if __name__ == '__main__':

    from cliente import Cliente

    aplica = Aplicador_modelos()

    cliente1 = Cliente(  nome = 'João'
                        , idade = 30
                        , score = 100
                        , regiao = 'SP'
                        , salario=2000.0)

    cliente2 = Cliente(  nome = 'Paulo'
                        , idade = 29
                        , score = 1000
                        , regiao = 'SP'
                        , salario=5000.0)

    cliente3 = Cliente(  nome = 'Neilton'
                        , idade = 18
                        , score = 700
                        , regiao = 'SP'
                        , salario=2000.0)

    cliente4 = Cliente(  nome = 'Neilton'
                        , idade = 18
                        , score = 100
                        , regiao = 'SP'
                        , salario=2000.0)
    
    print aplica.calcula(cliente1)
    print aplica.calcula(cliente2)
    print aplica.calcula(cliente3)
    print aplica.calcula(cliente4)

Quando testamos o código acima, não há qualquer erro, porem, estamos deixando a aplicação principal com a definição de estratégia de aplicação dos modelos.

Como reescrever o código para o padrão Strategy?

Nossa classe de clientes se mantém do mesmo modo. Mas, iremos criar um novo módulo que armazenará os modelos.


# -*- coding: UTF-8 -*-
# Dev: Jefferson Soares
# cliente.py

class Cliente(object):

    def __init__(self, nome, idade, score=None, regiao=None, salario=0.0):
        self.nome = nome
        self.idade = idade
        self.score = score
        self.regiao = regiao
        self.salario = salario

O módulo abaixo armazena todos os modelos que temos, cada modelo deve obrigatoriamente ter um método chamado “calcular”, e esse será o responsável por aplicar o modelo, verificar a regra e também retornar a próxima estratégia, seja ela qual for.

Em cada classe de modelo, o construtor recebe como parâmetro a próxima estratégia (modelo), assim poderemos retornar a próxima estratégia caso as regras não sejam atendidas.

Note que também teremos uma classe que não aplica nenhum modelo, ela é necessária para caso nenhuma das regras seja atendida.


# -*- coding: UTF-8 -*-
# Dev: Jefferson Soares
# modelos.py

class Modelo_idade(object):
  def __init__(self, proximo_modelo):
      self.__proximo_modelo = proximo_modelo

  def calcular(self, cliente):

    if(cliente.idade >= 30 and cliente.idade < 50):
        return 'Aplica modelo de idade'
    else:       
      return self.__proximo_modelo


class Modelo_salario(object):
  def __init__(self, proximo_modelo):
    self.__proximo_modelo = proximo_modelo

  def calcular(self, cliente):

    if(cliente.salario >= 5000):
      return 'Aplica modelo de salário'
    else:      
      return self.__proximo_modelo


class Modelo_score(object):
  def __init__(self, proximo_modelo):
    self.__proximo_modelo = proximo_modelo

  def calcular(self, cliente):

    if(cliente.score >= 500):
      return 'Aplica modelo de Score'
    else:      
      return self.__proximo_modelo


class Sem_modelo(object):
  def calcular(self, cliente):
    return 'Não aplica nenhum modelo.'

Agora nossa classe principal, Aplicador_modelo, poderá apenas chamar as classes na ordem que decidimos, e quem se encarregará da próxima estratégia é a classe do modelo que recebe a próxima estratégia da nossa aplicação principal.

Dessa forma podemos mudar a ordem ou incluir novos elementos facilmente.


# -*- coding: UTF-8 -*-
# Dev: Jefferson Soares
# aplica_modelo.py

from modelos import Modelo_idade, Modelo_salario, Modelo_score, Sem_modelo

class Aplicador_modelos(object):

    def calcula(self, cliente):

        modelo = Modelo_idade(
                    Modelo_salario(
                        Modelo_score(
                            Sem_modelo().calcular(cliente)
                        ).calcular(cliente)
                    ).calcular(cliente)
                ).calcular(cliente)
        
        return modelo

if __name__ == '__main__':

    from cliente import Cliente

    aplica = Aplicador_modelos()

    cliente1 = Cliente(  nome = 'João'
                        , idade = 30
                        , score = 100
                        , regiao = 'SP'
                        , salario=2000.0)

    cliente2 = Cliente(  nome = 'Paulo'
                        , idade = 29
                        , score = 1000
                        , regiao = 'SP'
                        , salario=5000.0)

    cliente3 = Cliente(  nome = 'Neilton'
                        , idade = 18
                        , score = 700
                        , regiao = 'SP'
                        , salario=2000.0)

    cliente4 = Cliente(  nome = 'Neilton'
                        , idade = 18
                        , score = 100
                        , regiao = 'SP'
                        , salario=2000.0)
    
    print aplica.calcula(cliente1)
    print aplica.calcula(cliente2)
    print aplica.calcula(cliente3)
    print aplica.calcula(cliente4)

Como podemos observar, há uma organização natural quando utilizamos Strategy em nosso código.

Conclusão

Por mais que o código fora do padrão ainda seja funcional, podemos ter problemas quando quisermos fazer alguma manutenção ou mudança em algum dos modelos.

Utilizando o padrão Strategy podemos tanto aumentar a quantidade de modelos quanto diminuir de maneira organizada e simples.

Bom, espero que tenham gostado, há ainda outros padrões que podemos utilizar para organizar melhor nossos códigos e que facilitarão modificações no futuro.

Até a próxima! 🙂


Jefferson Soares

Olá! Sou Jefferson. Trabalho com: Dados, Dashboards, SQL, SAS, Python e muito mais! Criei esse cantinho para postar alguns conhecimentos. :)

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comentários
Inline Feedbacks
View all comments