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! 🙂