Esse é o primeiro artigo de uma série. Estou escrevendo essa série com o intuíto de me ajudar a absorver melhor o conteúdo do livro ou , que vai tornar o código mais orientado a objetos e extensível, respeitando o príncipio de Open/Closed, uma vez que adicionar mais formas de calcular taxas não afetará o código de Transaction.
Problema
Usar strings para representar tipos de transações pode tornar o código menos robusto e mais difícil de manter. Além disso, se o cálculo da taxa de transação se tornar mais complexo, essa abordagem pode levar a um código com muitos condicionais. A técnica Replace Type Code with State/Strategy pode ser aplicada aqui para melhorar a organização do código.
Aplicando Replace Type Code with State/Strategy
Vamos substituir o uso de strings para representar tipos de transação por uma hierarquia de classes, onde cada classe representa um tipo específico de transação e encapsula o comportamento correspondente.
Passo 1: Criar a Hierarquia de Classes
Primeiro, criamos uma classe base TransactionType e subclasses específicas para cada tipo de transação (Withdrawal e Deposit), que implementam o método calculate_fee de maneira diferente.
from abc import ABC, abstractmethod
class TransactionType(ABC):
@abstractmethod
def calculate_fee(self, amount):
pass
class Withdrawal(TransactionType):
def calculate_fee(self, amount):
if amount > 100:
return amount * 0.02
return 0
class Deposit(TransactionType):
def calculate_fee(self, amount):
if amount > 1000:
return amount * 0.01
return 0
Passo 2: Refatorar a Classe Transaction
Agora, modificamos a classe Transaction para utilizar uma instância de TransactionType em vez de uma string:
class Transaction:
def __init__(self, transaction_type, amount):
self.transaction_type = transaction_type
self.amount = amount
def calculate_fee(self):
return self.transaction_type.calculate_fee(self.amount)
Agora, ao invés de verificar o tipo de transação usando strings, Transaction delega o cálculo da taxa à instância da estratégia apropriada.
Benefícios da Refatoração
- Eliminação de Condicionais: O código que antes usava condicionais para determinar o comportamento agora usa polimorfismo, onde cada classe de transação encapsula seu próprio comportamento.
- Maior Flexibilidade: Novos tipos de transação podem ser adicionados simplesmente criando novas subclasses de TransactionType, sem precisar modificar a classe Transaction ou adicionar mais condicionais.
- Código Mais Limpo e Manutenível: O uso de objetos em vez de códigos de tipo torna o código mais organizado e facilita futuras expansões ou modificações.
Replace Conditional with Polymorphism
Replace Conditional with Polymorphism é uma técnica de refatoração que substitui condicionais complexas (como if-else ou switch-case) por polimorfismo. Isso é feito criando uma hierarquia de classes onde cada classe concreta implementa um comportamento específico. Essa abordagem melhora a legibilidade, facilita a manutenção e reduz a complexidade do código.
Podemos usar o exemplo anterior para analisar essa técnica.
Como estamos fazendo baseado em uma estrutura de classes, podemos usar o conceito de polimorfismo, que é uma técnica de refatoração que substitui condicionais complexas (como if-else ou switch-case) por polimorfismo.
Isso é possível pois a classe que chama o método depende apenas da classe abstrata TransactionType, e não da sua implementação concreta.
class Transaction:
def __init__(self, transaction_type, amount):
self.transaction_type = transaction_type
self.amount = amount
def calculate_fee(self):
return self.transaction_type.calculate_fee(self.amount) # Ao invés de condicionais, Transaction delega o cálculo da taxa à instância da estratégia apropriada.
def calculate_fees(self):
total_fees = 0
for transaction in self.transactions:
total_fees += transaction.calculate_fee() # Não sabe qual o tipo de transação, só sabe que a transação tem um método calculate_fee.
return total_fees
Isso faz com que não seja necessario condicionais, uma vez que ao receber um objeto TransactionType, o método calculate_fee pode ser chamado de forma independente da classe concreta que implementa o método.
Ao instanciar uma Transcation, informamos qual a classe TransactionType que vai ser usada para calcular a taxa, e o método Transaction chama o método calculate_fee da instância da classe TransactionType.
No exemplo anterior, tinhamos:
- Condicionais Existentes:
O código original usava condicionais para verificar o tipo de transação ('withdrawal' ou 'deposit') e, com base nisso, aplicava uma lógica diferente no cálculo da taxa.
- Refatoração com Polimorfismo:
Em vez de manter esses condicionais, criamos uma hierarquia de classes (TransactionType, Withdrawal, e Deposit), onde cada classe implementa o método calculate_fee de forma distinta, de acordo com o tipo de transação.
Esse é o princípio central do Replace Conditional with Polymorphism: o comportamento variável é tratado por meio de classes distintas e polimorfismo, em vez de condicionais.
- Respeita o princípio de Open/Closed:
Adicionar novos comportamentos é uma questao de implmentar novas subclasses de TransactionType, e não de modificar a classe Transaction. Isso é um sinal de que o código deve ser aberto para extensão, mas fechado para modificação.
Replace Temp With Query
A técnica de refatoração "Replace Temp With Query" é utilizada quando você tem uma variável temporária que armazena o resultado de uma expressão. A ideia é substituir essa variável por uma chamada de método, melhorando a legibilidade e a manutenibilidade do código.
Exemplo
Considere um sistema de gerenciamento de pedidos em uma loja online. Temos uma classe Order que calcula o preço total de um pedido, incluindo descontos.
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def calculate_total(self):
base_price = self.quantity * self.item_price
quantity_discount = 0
if self.quantity > 100:
quantity_discount = base_price * 0.1
shipping = base_price * 0.1
return base_price - quantity_discount + shipping
Obs:
Repare como temos várias variáveis temporárias (base_price, quantity_discount, shipping) que armazenam resultados intermediários. Isso pode tornar o código mais difícil de entender e manter, especialmente se esses cálculos forem necessários em outros lugares.
Problema
O método calculate_total usa várias variáveis temporárias para cálculos intermediários. Isso pode dificultar a compreensão do código e levar a duplicações se esses cálculos forem necessários em outros lugares.
Aplicando Replace Temp With Query
Vamos refatorar o código substituindo cada variável temporária por uma chamada de método:
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def calculate_total(self):
return self.base_price() - self.quantity_discount() + self.shipping()
def base_price(self):
return self.quantity * self.item_price
def quantity_discount(self):
return self.base_price() * 0.1 if self.quantity > 100 else 0
def shipping(self):
return self.base_price() * 0.1
Obs:
Repare que agora não temos mais variáveis temporárias. Cada cálculo intermediário foi transformado em um método. Isso torna o código mais legível e cada parte do cálculo pode ser facilmente reutilizada ou testada individualmente.
Benefícios da Refatoração
Legibilidade Melhorada: Cada parte do cálculo agora tem um nome descritivo, tornando o código mais fácil de entender.
Reusabilidade: Se precisarmos do preço base ou do desconto em outro lugar, podemos simplesmente chamar o método correspondente.
Facilidade de Manutenção: Se a lógica de cálculo mudar (por exemplo, a regra de desconto), só precisamos modificar o método específico.
Testabilidade: Cada método pode ser testado independentemente, facilitando a criação de testes unitários.
Considerações
Performance: Em alguns casos, substituir variáveis por chamadas de método pode ter um pequeno impacto na performance, especialmente se o método for chamado muitas vezes em um loop.
Complexidade: Para cálculos muito simples, criar um método separado pode parecer excessivo. Use seu julgamento para decidir quando a refatoração vale a pena.
Ao aplicar "Replace Temp With Query", você está essencialmente transformando dados (variáveis) em comportamento (métodos), o que geralmente leva a um design mais orientado a objetos e mais flexível.
SOCIAL SHARE CARD GENERATOR