Gostaria de receber alguns esclarecimentos sobre como implementar adequadamente a 'Composição sobre Herança'. Acho que tenho uma noção da teoria, mas na prática tenho dificuldade em entender como isso não leva à duplicação de código.
Tenho o seguinte exemplo (é Java): suponha que temos uma classe animal abstrata:
abstract class Animal {
protected void eat() {
// Common eating implementation
}
protected void sleep() {
// Common sleeping implementation
}
}
E queremos construir animais voadores e nadadores. Entendo que a melhor maneira de fazer isso para seguirmos o LSP é com interfaces para cada um:
interface Flyer {
void fly();
}
interface Swimmer {
void swim();
}
Então teríamos
class Salmon extends Animal implements Swimmer {
@Override
public void swim() {
// Swim implementation
}
}
class Sparrow extends Animal implements Flyer {
@Override
public void fly() {
// Fly implementation
}
}
Mas então temos um novo requisito para Magpie
que voe da mesma forma que um pardal. Criaríamos a classe, não muito diferente de Sparrow
:
class Magpie extends Animal implements Flyer {
@Override
public void fly() {
// Same exact fly implementation as Sparrow.fly
}
}
Imagine, para o propósito deste exercício, que a implementação fly é muito complexa com integração de banco de dados, registro, etc. - isso leva a uma carga de código duplicado e se adicionarmos mais pássaros ou peixes, precisaremos duplicar o código ainda avançar.
Houve também a ideia de ter uma classe abstrata para o mesmo tipo de panfletos como
abstract class FlyingBird extends Animal implements Flyer
e temos a implementação comum lá, mas e se precisarmos criar alguns deles e houver um animal que precise estender dois deles? É uma ladeira escorregadia...
Existe alguma maneira de evitar isso? Ou estou errando o alvo em algum lugar?