Como sabemos que favorecer a composição em detrimento da generalização é sempre a escolha certa?

9

Quer um objeto exista fisicamente ou não, podemos optar por modelá-lo de diferentes maneiras. Poderíamos usar arbitrariamente generalização ou composição em muitos casos. No entanto, o princípio do GoF de "favorecer a composição sobre a generalização [sic]" nos guia a usar a composição. Portanto, quando modelamos, por exemplo, uma linha, criamos uma classe que contém dois membros PointA e PointB do tipo Point (composition) em vez de estender Point (generalization). Este é apenas um exemplo simplificado de como podemos escolher arbitrariamente composição ou herança para modelar, apesar de os objetos serem geralmente muito mais complexos.

Como sabemos que esta é a escolha certa? Importa pelo menos porque poderia haver uma tonelada de refatoração a ser feita se estiver errado?

CarneyCode
fonte
9
Seu exemplo não funciona realmente porque você não pode dizer que uma linha é um ponto e, portanto, falha no Princípio de Substituição de Liskov e a herança não é apropriada.
Dean Harding
@ Dean: Como você provavelmente sabe, o exemplo não é central para apontar. Para o registro, uma linha pode ser representada como uma equação definida através de dois pontos.
Car11
11
@Ongo: Parece que você perdeu totalmente o objetivo.
CarneyCode
Não é estender a especialização em vez da generalização?
Tulains Córdova 26/10/12

Respostas:

17

Nem sempre é a escolha certa. É o mais favorável na maioria dos casos. Quando um modelo composto requer alteração ou extensão, ele é consideravelmente mais resistente a isso, porque você pode alterar a composição sem medo de afetar outras classes inadvertidamente.

Como nós sabemos disso? Da experiência e da experiência dos outros.

Eu já vi muitas situações em que uma hierarquia de classes maciça cresceu a partir do que começou como um conceito simples, e é aqui que sua refatoração importante se torna necessária. Enquanto isso, nunca vi uma situação em que uma estrutura de composição precisasse ser refatorada em herança.

Mas minha evidência anedótica não deve ser suficiente. Basta olhar na internet e um bom número de pessoas teve as mesmas experiências.

pdr
fonte
Estou gostando; faça uma votação.
CarneyCode
+1 em "Nunca vi uma situação em que uma estrutura de composição precisasse ser refatorada em herança".
Kazark 26/11/12
7

Se você deseja uma regra de ouro mais forte além de "favorecer a composição sobre a herança", sugiro algo como isto:

Das duas maneiras de especializar um objeto - Herança e Composição - você deve usar a herança somente quando precisar que seu objeto seja polimórfico (substituível) para a classe base que você está especializando.

No entanto, como todas as regras de ouro, depois de entender a regra - você estará livre para violar a regra :)

Stephen Bailey
fonte
0

Não vejo como a composição e a generalização são alternativas e não consegui encontrar a citação .
Composição e herança são (para obter especialização), e pode-se argumentar, que abstração e generalização são (para alcançar modularidade).

O que você deseja é que seu design seja simples e plausível, que são duas qualidades que são inerentemente facilmente mensuráveis.
No seu caso, parece mais plausível compor uma linha de dois pontos em vez de estendê-la, porque você definiria naturalmente uma linha por dois pontos, em vez de estender o conceito de um ponto.

back2dos
fonte
É mais natural? Uma linha não tem mais dois pontos do que um único ponto estendido?
CarneyCode
2
Eu nunca vi uma definição de linha que não inclua o fato de ser feita com mais de 2 pontos. Mesmo usando uma equação de uma linha se você tiver apenas 1 x valor no seu domínio de números, então é um ponto, não uma linha.
Rig
0

O favor acontece quando os dois candidatos se qualificam. O problema indicado na pergunta falha nesta conta, porque ela possui apenas uma opção de composição.

"A composição também pode usar generalização". Essa afirmação faz sentido para nós? Caso contrário, ainda não estamos prontos para entender a regra do "favor".

Por que estamos favorecendo a Composition é que a Composition oferece mais possibilidades de extensão / flexibilidade do que generalização. Essa extensão / flexibilidade refere-se principalmente à flexibilidade de tempo de execução / dinâmica (que é obtida com a combinação de interfaces e composição).

O benefício não é imediatamente visível. Para ver o benefício, você precisa aguardar a próxima solicitação de mudança inesperada. Portanto, na maioria dos casos, aqueles que aderiram à generalização falham quando comparados aos que adotaram a composição (exceto um caso óbvio mencionado posteriormente). Daí a regra. Do ponto de vista do aprendizado, se você pode implementar uma injeção de dependência com êxito, deve saber qual delas favorecer e quando. A regra ajuda você a tomar uma decisão quando não tiver certeza de qual escolher. Novamente, você deve poder ver as duas opções em primeiro lugar para favorecer uma.

Resumo: Composição: o acoplamento é reduzido apenas com algumas coisas menores que você conecta em algo maior, e o objeto maior apenas chama o objeto menor de volta. Geração: Do ​​ponto de vista da API de terceiros, definir que um método pode ser substituído é um compromisso mais forte do que definir que um método pode ser chamado (vitória certa para a Generalização). E nunca esqueça que, com a composição, você também está usando generalização, a partir de uma interface em vez de uma grande classe. Infelizmente, todo o crédito vai para a composição.

nuvens azuis
fonte
-4

Hoje eu escrevi cerca de 300 LoC. E não me lembro de nenhum princípio que pudesse violar e não violei. Refatorar vai salvar minha alma, espero.

Se a abstração é ditada pela API da parte 3d - geralmente é correto usar a herança. No design doméstico, a entidade deve ser abstrata ou selada. A entidade abstrata potencialmente deve ter cerca de 3 herdeiros. Não gosto de pontos de extensão de método virtual. Tudo acima é sobre o meu gosto. Não estou falando sobre abstração de interface, que é uma história completamente diferente.

Andrew_B
fonte