Implementando o Padrão Simple Factory com Python

O que é o Padrão Simple Factory?

O padrão Simple Factory (Fábrica Simples) é um padrão de projeto criacional que encapsula a lógica de criação de objetos em uma classe centralizada. Em vez de instanciar objetos diretamente, o cliente delega essa responsabilidade a uma classe fábrica, que decide qual classe concreta instanciar com base em parâmetros fornecidos. As classes criadas por essa fábrica geralmente compartilham uma interface comum ou uma classe base.

Estrutura do Padrão Simple Factory

No coração do padrão Simple Factory está a classe "Fábrica". Esta classe possui um método estático ou de instância que recebe um tipo ou identificador como argumento e retorna uma instância da classe de produto correspondente. A fábrica gerencia a lógica interna para determinar qual objeto criar, isolando o código cliente dos detalhes de instanciação.

Exemplo Prático: Uma Calculadora Simples

Vamos demonstrra o Simple Factory construindo uma calculadora básica em Python que suporta operações de adição, subtração, multiplicação e divisão.

Definição da Classe Base para Operações

Primeiro, criamos uma classe base que define a interface comum para todas as operações matemáticas. Ela também se encarrega de converter as entradas para números de ponto flutuante e declara um método abstrato para a execução da operação.

class OperacaoMatematica:
    """
    Classe base abstrata para todas as operações matemáticas.
    Define a interface comum para as operações e trata a conversão dos operandos.
    """
    def __init__(self, str_operando1: str, str_operando2: str):
        self._operando1 = float(str_operando1)
        self._operando2 = float(str_operando2)

    def executar(self) -> float:
        """
        Método abstrato que deve ser implementado pelas subclasses
        para realizar a operação específica.
        """
        raise NotImplementedError("O método 'executar' deve ser implementado por subclasses.")

Classes Concretas para Operações Específicas

Agora, implementamos as classes para cada operação, que herdam de OperacaoMatematica e fornecem sua própria implementação do método executar().

class OperacaoSoma(OperacaoMatematica):
    """Implementa a operação de adição."""
    def executar(self) -> float:
        return self._operando1 + self._operando2

class OperacaoSubtracao(OperacaoMatematica):
    """Implementa a operação de subtração."""
    def executar(self) -> float:
        return self._operando1 - self._operando2

class OperacaoMultiplicacao(OperacaoMatematica):
    """Implementa a operação de multiplicação."""
    def executar(self) -> float:
        return self._operando1 * self._operando2

class OperacaoDivisao(OperacaoMatematica):
    """Implementa a operação de divisão, com verificação de divisão por zero."""
    def executar(self) -> float | str: # Retorna float ou string em caso de erro
        if self._operando2 == 0:
            return "Erro: Divisão por zero não é permitida!"
        return self._operando1 / self._operando2

A Classe Fábrica Simples

A seguir, criamos a classe FabricaDeOperacoes. Ela é responsável por instanciar a classe de operação correta com base no símbolo do operador fornecido pelo usuário. Para melhorar a extensibilidade e evitar uma longa cadeia de if/elif, podemos usar um dicionário para mapear operadores a classes.

class FabricaDeOperacoes:
    """
    A Fábrica Simples que cria instâncias de OperacaoMatematica.
    """
    # Mapeamento de símbolos de operador para as classes de operação correspondentes
    _operacoes_disponiveis = {
        '+': OperacaoSoma,
        '-': OperacaoSubtracao,
        '*': OperacaoMultiplicacao,
        '/': OperacaoDivisao
    }

    @staticmethod
    def criar_operacao(simbolo_operador: str, val1_str: str, val2_str: str) -> OperacaoMatematica:
        """
        Cria e retorna uma instância de uma operação matemática específica
        com base no símbolo do operador.

        Args:
            simbolo_operador (str): O símbolo que representa a operação (ex: '+', '-', '*', '/').
            val1_str (str): O primeiro operando como string.
            val2_str (str): O segundo operando como string.

        Returns:
            OperacaoMatematica: Uma instância da operação solicitada.

        Raises:
            ValueError: Se o símbolo do operador for desconhecido.
        """
        tipo_operacao = FabricaDeOperacoes._operacoes_disponiveis.get(simbolo_operador)
        if tipo_operacao:
            return tipo_operacao(val1_str, val2_str)
        else:
            raise ValueError(f"Operador '{simbolo_operador}' não reconhecido. Use '+', '-', '*' ou '/'.")

Código Cliente: Utilizando a Fábrica

Finalmente, o código cliente interage com a fábrica para obter a operação desejada e executar o cálculo, sem precisar conhecer os detalhes de implementação das classes de operação concretas.

if __name__ == "__main__":
    print("--- Calculadora Simples ---")
    try:
        primeiro_num_str = input("Digite o primeiro número: ")
        segundo_num_str = input("Digite o segundo número: ")
        operador_str = input("Digite o operador (+, -, *, /): ")

        # A fábrica cria a instância da operação para nós
        operacao_a_executar = FabricaDeOperacoes.criar_operacao(
            operador_str,
            primeiro_num_str,
            segundo_num_str
        )

        # Executamos a operação e exibimos o resultado
        resultado_calculo = operacao_a_executar.executar()
        print(f"O resultado é: {resultado_calculo}")

    except ValueError as ve:
        print(f"Erro de entrada: {ve}")
    except NotImplementedError as nie:
        print(f"Erro de implementação: {nie}")
    except Exception as e:
        print(f"Ocorreu um erro inesperado: {e}. Verifique as entradas.")

Vantagens e Desvantagens do Simple Factory

Vantagens

  • Desacoplamento: O cliente é desacoplado das classes de produto concretas. Ele interage apenas com a fábrica e a interface comum do produto.
  • Centralização da Lógica de Criação: A lógica para decidir qual objeto criar é concentrada em um único local, facilitando a manutenção e a modificação.
  • Facilita a Leitura: Para o cliente, o código se torna mais legível, pois ele não precisa se preocupar com os detalhes de instanciação dos objetos.
  • Gerenciamento de Complexidade: Ideal para situações onde a lógica de criação de objetos é complexa ou envolve várias etapas.

Desvantagens

  • Viola o Princípio Aberto/Fechado (Open/Closed Principle): Adicionar um novo tipo de produto requer modificar o código da fábrica, o que pode levar a alterações frequentes na classe fábrica se houver muitos produtos.
  • Risco de God Object: Se a fábrica tiver que lidar com a criação de muitos tipos de objetos diferentes, ela pode se tornar muito complexa e difícil de manter.
  • Não Extensível por Herança: Como o método de criação é frequentemente estático, a fábrica não pode ser estendida ou modificada através de herança facilmente.

Quando Usar o Simple Factory

O padrão Simple Factory é mais adequado nas seguintes situações:

  • Quando a classe fábrica é responsável por criar um número limitado de objetos. Isso evita que a lógica de criação se torne excessivamente complexa.
  • Quando o cliente precisa criar objetos, mas não se importa com os detalhes de sua criação, apenas com o tipo de objeto que deseja. O cliente fornece um parâmetro para a fábrica e recebe uma instância válida sem saber como ela foi construída.

Tags: Python design patterns Simple Factory Padrões Criacionais OOP

Publicado em 7-23 01:19