Eu estava lendo sobre padrões de design e li que o padrão de design de protótipo acaba com a subclassificação excessiva.
Por que a subclasse é ruim? Que vantagem o uso de um protótipo provocaria sobre a subclasse?
Eu estava lendo sobre padrões de design e li que o padrão de design de protótipo acaba com a subclassificação excessiva.
Por que a subclasse é ruim? Que vantagem o uso de um protótipo provocaria sobre a subclasse?
Respostas:
Por que a subclasse é muito ruim
"Demais" é uma decisão judicial; mas tire isso de mim é ruim. O código com o qual trabalho tem herança DEEP e é muito difícil de ler, entender, seguir, rastrear, depurar etc. etc. É efetivamente impossível escrever código de teste para esse material.
Padrão de protótipo elimina a subclassificação
Ou a pergunta significa "acaba com muita subclasse?". Esse padrão exige a clonagem de um objeto "modelo" para evitar subclassificação, pelo menos nesse ponto. Não existe uma regra que diga que o "modelo" não pode ser uma subclasse.
Favorecer a composição sobre a herança
Essa idéia aqui também inclui delegação e agregação. Seguir essa heurística significa que seu software tende a ser mais flexível, mais fácil de manter, estender e reutilizar.
Quando uma classe é composta de partes, você pode substituí-las no tempo de execução . Isso tem um efeito profundo na testabilidade.
Teste é mais fácil . Você pode usar peças falsas (ou seja, "zombarias", "duplas" e outras falas de teste). A herança profunda do nosso código significa que devemos instanciar toda a hierarquia para testar qualquer parte dela. No nosso caso, isso não é possível sem a execução do código em seu ambiente real. Por exemplo, precisamos de um banco de dados para instanciar objetos de negócios.
As mudanças vêm com efeitos colaterais e incerteza - Quanto mais "básica" a classe, mais difundidos são os efeitos, bons ou ruins. Pode haver alterações desejadas que você não ousa fazer devido à incerteza dos efeitos colaterais. Ou uma mudança que seja boa para algum lugar da nossa cadeia de herança é ruim para outra. Esta é certamente a minha experiência.
Penso que uma das razões pelas quais isso foi considerado uma prática ruim é que, com o tempo, cada subclasse pode conter atributos e métodos que não fazem sentido para esse objeto, à medida que você avança na cadeia (em uma hierarquia mal desenvolvida). Você pode acabar com uma classe inchada que contém itens que não fazem sentido para essa classe.
Eu sou agnóstico sobre isso eu mesmo. Parece dogmático dizer que é ruim. Qualquer coisa é ruim quando mal utilizada.
Duas razões.
É desempenho em tempo de execução. Sempre que você invocar uma subclasse de uma superclasse, você está fazendo uma chamada de método. Se você aninhou cinco classes e invocou um método na classe base, estará fazendo cinco invocações de método. A maioria dos compiladores / tempos de execução tentará reconhecer isso e otimizar as chamadas extras de distância, mas é seguro fazer isso apenas em casos muito simples.
É a sanidade de seus colegas programadores. Se você aninhar cinco classes, eu tenho que examinar todas as quatro classes pai para estabelecer que você está realmente chamando um método na classe base, isso fica entediante. Talvez eu também precise percorrer cinco invocações de método ao depurar o programa.
"Demais" é uma decisão judicial.
Como na maioria das coisas em programação, a subclasse não é inerentemente ruim quando faz sentido. Quando o modelo da sua solução de problemas faz mais sentido como hierarquia do que como composição de partes, a subclasse pode ser a opção preferida.
Dito isto, favorecer a composição sobre a herança é uma boa regra de ouro.
Quando você subclasse, o teste pode ser mais difícil (como observou o radarbob ). Em uma hierarquia profunda, isolar o código problemático é mais difícil, pois a instanciação de uma subclasse exige a inserção de toda a hierarquia da superclasse e um monte de código adicional. Com uma abordagem composicional, você pode isolar e testar cada componente do seu objeto agregado com testes de unidade, objetos simulados etc.
A subclassificação também pode causar a exposição de detalhes da sua implementação que você pode não querer. Isso dificulta o controle do acesso à representação subjacente dos seus dados e o vincula a uma implementação específica do seu modelo de apoio.
Um exemplo em que posso pensar é java.util.Properties, que herda de Hashtable. A classe Properties destina-se apenas a armazenar pares String-String (isso é observado nos Javadocs). Por causa da herança, os métodos put e putAll do Hashtable foram expostos aos usuários das Propriedades. Enquanto a maneira "correta" de armazenar uma propriedade é com setProperty (), não há absolutamente nada impedindo que um usuário chame put () e transmitindo uma String e um Objeto.
Basicamente, a semântica das Propriedades é mal definida por causa da herança. Além disso, se alguém quiser alterar o objeto de suporte para Propriedades, o impacto terá um impacto muito maior no código que usa Propriedades do que se as Propriedades tivessem adotado uma abordagem mais composicional.
A herança pode quebrar o encapsulamento em alguns casos, e se isso acontecer, é ruim. Leia para mais.
Nos velhos tempos, sem herança, uma biblioteca publicava uma API e um aplicativo usando o que é publicamente visível, e a biblioteca agora tem seu próprio modo de lidar com as mudanças internas que continuam mudando sem interromper o aplicativo.
À medida que as necessidades evoluem, a biblioteca pode evoluir e ter mais clientes; ou pode fornecer funcionalidade estendida herdando de sua própria classe com outros tipos. Por enquanto, tudo bem.
No entanto, o fundamental é que, se um aplicativo assume a classe e começa a subclassificá-la, agora uma subclasse é realmente um cliente e não um parceiro interno. No entanto, diferentemente de uma maneira clara e inequívoca em que a API pública é definida, a subclasse agora está muito mais profunda na biblioteca (acesso a todas as variáveis privadas e assim por diante). Ainda não é ruim, mas um personagem desagradável pode fazer uma ponte com uma API que é muito dentro do APP e também na biblioteca -
Em essência, embora isso nem sempre seja o caso, mas,
É fácil usar indevidamente a herança para quebrar o encapsulamento
Permitir subclassificação pode estar errado se esse ponto.
Resposta rápida e curta:
Depende do que você está fazendo.
Resposta Aborrecida Estendida:
Um desenvolvedor pode criar um aplicativo. usando modelos de subclasse ou (prototípicos). Às vezes, uma técnica funciona melhor. Às vezes, a outra técnica funciona melhor.
A escolha da técnica funciona melhor, também requer considerar se um cenário de "Herança Múltipla" está presente. Mesmo que a linguagem de programação suporte apenas herança única, modelos ou protótipos.
Nota complementar:
Algumas respostas estão relacionadas à "herança múltipla". Embora não seja a questão principal, ela está relacionada ao assunto. Existem vários posts semelhantes sobre "herança múltipla versus composição". Eu tive um caso em que misturo os dois.