Por que este ShapeFactory usa instruções condicionais para determinar qual objeto instanciar. Não precisamos modificar o ShapeFactory se quisermos adicionar outras classes no futuro? Por que isso não viola o princípio aberto e fechado?
Por que este ShapeFactory usa instruções condicionais para determinar qual objeto instanciar. Não precisamos modificar o ShapeFactory se quisermos adicionar outras classes no futuro? Por que isso não viola o princípio aberto e fechado?
Respostas:
A sabedoria convencional orientada a objetos é evitar if
declarações e substituí-las pelo despacho dinâmico de métodos substituídos nas subclasses de uma classe abstrata. Por enquanto, tudo bem.
Mas o objetivo do padrão de fábrica é dispensar você de conhecer as subclasses individuais e trabalhar apenas com a superclasse abstrata . A idéia é que a fábrica saiba melhor que você qual classe específica instanciar, e será melhor trabalhar apenas com os métodos publicados pela superclasse. Isso geralmente é verdade e um padrão valioso.
Portanto, não há como escrever uma classe de fábrica renunciar às if
instruções. Isso mudaria o fardo de escolher uma classe específica para o chamador, que é exatamente o que o padrão deve evitar. Nem todos os princípios são absolutos (na verdade, nenhum princípio é absoluto) e, se você usar esse padrão, presumiria que o benefício dele é maior do que o benefício de não usar um if
.
if
s. Veja a resposta de @ BЈовић para um exemplo simples de como conseguir isso. Votado.
O exemplo provavelmente usa uma declaração condicional porque é a mais simples. Uma implementação mais complexa pode usar um mapa ou configuração ou (se você quiser mesmo) algum tipo de registro em que as classes possam se registrar. No entanto, não há nada de errado em usar uma condição se o número de classes for pequeno e mudar com pouca frequência.
Estender o condicional para adicionar suporte a uma nova subclasse no futuro seria, de fato, estritamente falando, uma violação do princípio de aberto / fechado. A solução "correta" seria criar uma nova fábrica com a mesma interface. Dito isto, a adesão ao princípio O / C deve sempre ser ponderada em relação a outros princípios de design, como o KISS e o YAGNI.
Dito isto, o código exibido é claramente um exemplo de código que é projetado para mostrar o conceito de fábrica e nada mais. Por exemplo, é um estilo muito ruim retornar nulo como o exemplo, mas o tratamento de erros mais elaborado apenas obscureceria o ponto. O código de exemplo não é um código de qualidade de produção; você não deve esperar que seja.
O próprio padrão não viola o Princípio Aberto / Fechado (OCP). No entanto, violamos o OCP quando usamos o padrão incorretamente.
A resposta simples para esta pergunta é a seguinte:
No exemplo fornecido, sua funcionalidade base suporta três formas: Círculo, Retângulo e Quadrado. Suponha que você precise apoiar Triângulo, Pentágono e Hexágono no futuro. Para fazer isso SEM violar o OCP, você deve criar uma fábrica adicional para dar suporte às novas formas (vamos chamar AdvancedShapeFactory
) e depois usar o AbstractFactory para decidir qual fábrica você precisa criar para criar as formas necessárias.
Se você está falando sobre o padrão Abstract Factory, a tomada de decisões geralmente não está no próprio Factory, mas no código do aplicativo. É esse código que escolhe qual fábrica de concreto instanciar e repassar para o código do cliente que usará objetos produzidos pela fábrica. Veja o final do exemplo de Java aqui: https://en.wikipedia.org/wiki/Abstract_factory_pattern
A tomada de decisão não implica necessariamente if
declarações. Poderia ler o tipo de fábrica concreto de um arquivo de configuração, derivá-lo de uma estrutura de mapa etc.
Se você pensa no Open-Close no nível da classe com esta fábrica, você está criando outra classe em seu sistema Open-Close, por exemplo, se você tem outra classe que usa uma Shape e calcula a área (exemplo típico), essa classe é OpenClose porque Ele pode calcular a área para novos tipos de formas sem modificação. Então você tem outra classe que desenha a forma, outra classe que toma as formas N e retorna a maior e você pode pensar em geral que as outras classes em seu sistema que lidam com formas são de abertura e fechamento (pelo menos sobre formas). Observando o projeto, a fábrica permite que o restante do sistema seja Aberto-Fechado e, obviamente, a própria fábrica NÃO É Aberto-Fechado.
É claro que você também pode abrir e fechar esta fábrica, através de algum tipo de carregamento dinâmico, e todo o sistema pode ser aberto / fechado (você pode adicionar novas formas, deixando cair alguns jarros no caminho de classe, por exemplo). Você precisa avaliar se essa complexidade extra vale a pena, dependendo do sistema que você está construindo, nem todos os sistemas precisam de recursos conectáveis e nem todo o sistema precisa ser completamente aberto / fechado.
O princípio aberto-fechado, como o princípio de substituição de Liskov, aplica-se a árvores de classes, a hierarquias de herança. No seu exemplo, a classe factory não está na árvore genealógica das classes instanciadas, portanto não pode violar essas regras. Haveria uma violação se seu GetShape (ou mais apropriadamente chamado, CreateShape) fosse implementado na classe base Shape.
Tudo depende de como você o implementa. Você pode usar std::map
para segurar ponteiros de função para funções que criam objetos. Então, o princípio de abrir / fechar não é violado. Ou mudar / caso.
De qualquer forma, se você não gostar do padrão de fábrica, sempre poderá usar a injeção de dependência.