-
-
Save alexandre/5bed42f22a639559fe14 to your computer and use it in GitHub Desktop.
""" | |
Vamos supor que temos um numero X de clientes e cada cliente tem 2 tipos de | |
fechamento diferentes: fechamento parcial e fechamento geral. | |
Além disso, cada cliente tem um padrão para esses arquivos (cada fechamento gera um), já que esses | |
arquivos serão utilizados em um sistema de terceiros. | |
A principio, eu pensei que apenas o pattern strategy solucionava o problema... | |
de acordo com o cliente eu devolvia uma classe especifica que geraria o | |
arquivo. Mas o problema é que eu esqueci que haviam 2 tipos de fechamento. | |
Com isso, eu pensei em utilizar herança. Por que? porque o processo básico | |
para chamar os arquivos de fechamento (indiferente do tipo) é o mesmo. | |
No final ficaria algo como: | |
>>> import exporters | |
>>> customer = Customer() | |
>>> exporters.ExporterA(customer=customer.name) | |
>>> exporter.generate_file() | |
>>> # o mesmo para o segundo tipo | |
>>> exporters.ExporterB(customer=customer.name) | |
>>> exporter.generate_file() | |
Obs.: Supondo que o meu entendimento do pattern strategy está correto. =] | |
""" | |
import collections | |
class Exporter(): | |
@property | |
def default_exporter(self): | |
raise NotImplementedError('Should have implemented this') | |
@property | |
def available_exporters(self): | |
raise NotImplementedError('Should have implemented this') | |
def __new__(cls, *args, **kwargs): | |
if 'customer' not in kwargs.keys(): | |
raise RuntimeError('You have to inform a customer name') | |
exporter_type = super(Exporter, cls).__new__(cls) | |
exporter = collections.defaultdict( | |
lambda: exporter_type.default_exporter, exporter_type.available_exporters) | |
return exporter[kwargs['customer']](*args, **kwargs) | |
class ExporterA(Exporter): | |
@property | |
def default_exporter(self): | |
return default_exporter_a | |
@property | |
def available_exporters(self): | |
return { | |
'Customer_foo': exporter_a_customer_foo, | |
'Customer_bar': exporter_a_customer_bar, | |
'Customer_ble': exporter_a_customer_ble, | |
} | |
class ExporterB(Exporter): | |
@property | |
def default_exporter(self): | |
return default_exporter_b | |
@property | |
def available_exporters(self): | |
return { | |
'Customer_foo': exporter_b_customer_foo, | |
'Customer_bar': exporter_b_customer_bar, | |
'Customer_ble': exporter_b_customer_ble, | |
} |
seguindo a ideia do Diego e o comentário sobre "trocar a estratégia em tempo de execução", essa é uma das grandes vantagens do Strategy.
closer.exporter = ExporterFile()
closer.close()
closer.exporter = ExporterConsole()
closer.close()
o seu objeto "closer" ainda é o mesmo, não há necessidade de instancia-lo novamente para aplicar outra estratégia.
Diego,
Deve existir um gap mesmo. =]
Mas a minha dúvida e também o motivo de usar herança é que além de ter o exporter diferente (A e B), cada cliente tem uma variação do A e uma variação do B. (available_exporters)
Eu poderia usar apenas 1 função com um dict para recuperar a variação(e.g. recuperar a variação do exporter A para o cliente foo) e passar isso para o exporter (da mesma forma que vc explicou a estratégia concreta). Faz sentido?
Eu acho que agora eu consegui entender melhor... eu não preciso de uma classe para cada tipo de exporter mesmo. Eu vou separar essa parte de recuperar a "variação" e simplesmente passar ela para o "closer".
Isso ai :)
Talvez suas estratégias sejam as "variações".
@drgarcia1986 e @daniloakamine-luizalabs
Apenas um rascunho mental enquanto eu não paro para ler...
# module exporters.py
import collections
EXPORTERS_A = collections.defaultdict(
lambda: default_a_exporter,
{
'Customer_foo': exporter_a_customer_foo,
'Customer_bar': exporter_a_customer_bar,
'Customer_ble': exporter_a_customer_ble,
}
)
EXPORTERS_B = collections.defaultdict(
lambda: default_b_exporter,
{
'Customer_foo': exporter_b_customer_foo,
'Customer_bar': exporter_b_customer_bar,
'Customer_ble': exporter_b_customer_ble,
}
)
class Closer:
def __init__(self, exporter):
self.exporter = exporter
def generate_file(self, *args, **kwargs):
return self.exporter.generate_file(*args, **kwargs)
customer = Customer()
# mesmo processo para o EXPORTERS_A e o EXPORTERS_B
exporter = exporters.EXPORTER_A[customer.name]()
closer = exporters.Closer(exporter)
closer.generate_file('xyz') # ... segue a vida
Brother, acredito que tenha um gap de conceito ou talvez eu não tenha entendido corretamente o problema.
É legal no strategy primeiro identificar os participantes (vou explicar mais ou menos como tenho na cabeça):
API
(ou interface) da estratégia (como em python usamos duck typing, não precisamos criar essa interface).Vou tentar fazer uma exemplo com o seu problema, vou imaginar que esse
fechamento
possa ser chamado deCloser
e o que varia (as estratégias) são os exportadores, ouExporter
.(vou digitar direto aqui no comentário, algo pode estar errado e fora do pep8 😄 )
Notw que para o usuário da classe
Closer
é indiferente qual estratégia estáativa
no momento? a api não muda por causa disso.Um detalhe é que no python, nesse exemplo simples poderia substituir as classes de estratégia concreta por simples funções (já que não estou guardando estado na classe, sendo assim, usar classes nesse caso seria desnecessário)
Sobre o
processo básico de chamar os arquivos de fechamento
, acredito que isso seja uma tarefa para o context (já que é independente das estratégias)Ajudei ?