Interface x Heranca

Sou aprendiz e cai nesse dilema:

"No livro Design Patterns, logo no inicio os autores citam 2 regras de ouro. Uma é “evite heranca, prefira composicao”, e a outra “programe voltado a interface e nao a implementacao”.
Texto Retirado da Apostila da Caelum (JAVA e Orientacao a Objetos)

“Mesmo que eles(objetos) tivessem atributos em comum, utilizar interfaces é uma maneira muito mais elegante de modelar suas classes. Elas tambem trazem grandes vantagens em nao acoplar as classes(heranca traz muito acoplamento, muitos autores classicos dizem que em muitos casos herenca quebra o encapsulamento, pensamento o qual os autores dessa apostila concordam plenamente)”
Texto Retirado da Apostila da Caelum(JAVA e Orientacao a Objetos)

Agora vamos as duvidas:

1.Interface é uma particularidade do JAVA ou existe em todas as linguagens “orientadas a objetos” ?

  1. O que a primeira regra de ouro diz nao dobraria o código escrito ? :shock:

3.Os autores da caelum citam que mesmo se os objetos tivessem metodos em comum, usar interface é melhor.
Uai, a interface apenas me diz os metodos que as classes devem ter, logo teriam que ser escrito em cada uma delas e a declaracao dos atributos tambem ?? Caramba, essa afirmacao nao deixaria meu codigo bem maior?

  1. “heranca traz muito acoplamento, muitos autores classicos dizem que em muitos casos herenca quebra o encapsulamento, pensamento o qual os autores dessa apostila concordam plenamente” . Nao entendi completamente essa afirmacao, alguem me ajuda? Por que acoplamento é ruim?

Ficou no ar que heranca nao deve ser usada("…mesmo se os objetos tivessem metodos em comum…") :?:
Porem dizem que heranca deve ser usada na mesma apostila, já que diminui código e etc… :?:

Bom, sou iniciante em POO . Ainda nao ficaram claras algumas ideias. Peço a ajuda de todos. :?

Agradeco desde já,

Renan

Esqueci de perguntar…

Entao de acordo com que a apostila diz,

É melhor eu fazer uma interface e implementar tudo em cada classe do que fazer uma classe abstrata e herda-la? Se sim, porque??

Amigo, nunca ouviu falar no “Zen” da programação?

Você nunca deve ser radical - deve saber como as coisas funcionam, para saber usá-las quando é necessário ou melhor, e não as usar, quando é necessário ou melhor. Em determinadas situações, é bom usar herança, e em outras não é adequado usar herança.

Em determinadas situações, até usar GOTO é permitido - a única coisa é que VOCÊ DEVE SABER O QUE ESTÁ FAZENDO, e você também deve saber O QUE AS PESSOAS QUE VÃO MANTER SEU CÒDIGO vão fazer com ele.

Se você fez um sistema perfeito à base de herança e classes abstratas, mas sua manutenção é impossível se muita coisa não for quebrada ou substituída, então você sabe que herança não funciona bem, mas sim interfaces e “inversion of control”. Mas em muitas condições herança funciona perfeitamente. Isso se aprende com a prática.

EDIT - Este texto é engraçado, mas acho que não vai lhe ajudar muito:
http://www.canonical.org/~kragen/tao-of-programming.html

A herança é um recurso de oo que possibilita o reaproveitamento de código, onde temos uma classe pai e uma classe filha. Neste caso, a classe filha herda as características (atributos e métodos) públicos da classe pai.

Eu sei o que é herança amigo. De qualquer forma, muito obrigado!

Em algumas aplicações web que desenvolvo, faço uso da herança, em outros casos, implemento interfaces. Vai mais de sua intuição.

Mas usar interfaces quando há classes com os mesmos métodos e atributos aumentaria bastante o codigo.
Mas porque ainda assim o material da caelum e os livros de design patterns diz para evitar heranca?? :roll:

Sei que nao devemos ser radicais, mas gostaria de saber onde está o embasamento para fazer uma afirmação como essa sobre o uso de herança.

Att,

Renan

Não me prendo a afirmações de autores, nem mesmo recomendações feitas em apostilas. Vejo o que o projeto pede em um dado momento.

A palavra reservada interface, pelo que eu conheço, é particular do Java, mas o mecanismo não. Em C++, pode criar uma superclasse com métodos virtuais puros, que age como as interfaces em Java. Linguagens OO dinâmicas como Ruby ou Javascript, não necessitam do mecanismo de interface, pois seus métodos ssó são conhecidos em tempo de execução.

Pode se usar composição, o mecanismo é uma classe com métodos comuns que faz parte de outras classes que queiram utilizá-lo.

[quote=renan_]3.Os autores da caelum citam que mesmo se os objetos tivessem metodos em comum, usar interface é melhor.
Uai, a interface apenas me diz os metodos que as classes devem ter, logo teriam que ser escrito em cada uma delas e a declaracao dos atributos tambem ?? Caramba, essa afirmacao nao deixaria meu codigo bem maior?[/quote]

Pura visão religiosa! Cada caso é um caso. Mas lembre-se, às vezes pode-se usar composição ao invés de herança ou implementação de interface

Herança acopla por causa da restrição protected. A idéia de OO é cada classe cuidar dos seus atributos, mas com a herança, uma outra classe pode ganhar acesso aos dados de outra classe. Uma frase clássica da computação diz: “Alta coesão e baixo acoplamento”, ou seja, eu devo separar o sistema em pedaços, mas cada pedaço deve ter um sentido único e completo. Porém, os javeiros mais arraigados dizem: “Baixo acoplamento e… e… é só!”, daí surgem aquelas arquiteturas em camadas infinitas!

Bom, enfim: o excesso de acoplamento é ruim, porque uma pequena alteração pode ter consequencias em várias outras partes do código.

1 curtida

O que eu costumo fazer:

  • Prefiro interfaces
  • Só uso herança quando estritamente necessário
  • Normalmente é melhor fazer o seguinte:

a) Definir uma interface - exemplo: Canideo

interface Animal {
    void comer();
}
interface Canideo extends Animal {
    void rosnar(); 
}

b) Definir uma “implementação padrão” para essa interface (uma classe) - exemplo: CanideoImpl. Essa implementação pode ser usada com casca e tudo (sem herança direta). Exemplo:

/* Note que isto não é uma classe abstrata */
class CanideoImpl implements Canideo {
    public void comer() { ... }
    public void rosnar() { ... }
    /* Nem todos os canídeos latem, mas nem por isso vou definir isto como abstrato. Em vez disso: */
    public void latir() { throws NotImplementedException("latir não implementado na implementação padrão"); }
}
/* Note que isto não herda diretamente de CanideoImpl, que é só uma classe auxiliar de implementação. */
class Cao implements Canideo {
    private Canideo impl = new CanideoImpl();
    public void comer () { impl.comer(); }
    public void rosnar () { impl.rosnar(); }
    public void latir () { System.out.println ("au!"); }
}

c) Se precisar criar uma classe que também implemente essa interface (como Raposa), mas que tem algo um pouco diferente, você faz mais ou menos a mesma coisa que fiz para criar a classe Cao:

/* Note que isto não herda diretamente de CanideoImpl, que é só uma classe auxiliar de implementação. */
class Raposa implements Canideo {
    private Canideo impl = new CanideoImpl();
    public void comer () { impl.comer(); }
    public void rosnar () { System.out.println ("rhhh!"); }
    public void latir () { System.out.println ("rau!"); }
}

C# também usa a palavra “interface” com exatamente o mesmo sentido. Idem para o VB.NET.

O Eiffel prefere a herança múltipla, mas de uma maneira que acaba se assemelhando ao Java, não ao C++. Você pode escolher de onde e se quer herdar alguma coisa.

Leonardo3001 , muito obrigado por sua resposta , aprendi bastante com ela.

thingol, achei MUITO interessante como você costuma fazer. Mas isso faz o codigo ficar maior, né?

Curiosidade: Por que acha melhor fazer dessa forma thingol ?

Obrigado a todos que responderam! :wink:

Att,

Renan

programe o quanto mais proximo possivel for voltado para interfaces… do que para a herança… primeiro vc vai ter q analisar a situação e dai decidir desde q vc sabia os beneficios da herança com classes e da implementação usando interface que nao deixa de ser uma herança tb.

“A Herança deve ser planejada, ou então proibida.”

thingol,

Sobre o Fragile base class… acontece só quando é usada a herança ou quando usa interface pode acontecer também? Ou ainda, a interface é a saída pro problema?

[quote=thingol]O que eu costumo fazer:

  • Prefiro interfaces
  • Só uso herança quando estritamente necessário
  • Normalmente é melhor fazer o seguinte:

a) Definir uma interface - exemplo: Canideo

interface Animal {
    void comer();
}
interface Canideo extends Animal {
    void rosnar(); 
}

b) Definir uma “implementação padrão” para essa interface (uma classe) - exemplo: CanideoImpl. Essa implementação pode ser usada com casca e tudo (sem herança direta). Exemplo:

/* Note que isto não é uma classe abstrata */
class CanideoImpl implements Canideo {
    public void comer() { ... }
    public void rosnar() { ... }
    /* Nem todos os canídeos latem, mas nem por isso vou definir isto como abstrato. Em vez disso: */
    public void latir() { throws NotImplementedException("latir não implementado na implementação padrão"); }
}
/* Note que isto não herda diretamente de CanideoImpl, que é só uma classe auxiliar de implementação. */
class Cao implements Canideo {
    private Canideo impl = new CanideoImpl();
    public void comer () { impl.comer(); }
    public void rosnar () { impl.rosnar(); }
    public void latir () { System.out.println ("au!"); }
}

c) Se precisar criar uma classe que também implemente essa interface (como Raposa), mas que tem algo um pouco diferente, você faz mais ou menos a mesma coisa que fiz para criar a classe Cao:

/* Note que isto não herda diretamente de CanideoImpl, que é só uma classe auxiliar de implementação. */
class Raposa implements Canideo {
    private Canideo impl = new CanideoImpl();
    public void comer () { impl.comer(); }
    public void rosnar () { System.out.println ("rhhh!"); }
    public void latir () { System.out.println ("rau!"); }
}

[/quote]

Thingol, não leve isso pro lado pessoal, mas vou criticar sua proposta.

É o seguinte: nesse tipo de implementação, é melhor usar herança, porque você está lidando com uma relação do tipo especialização/generalização. Eu vejo intefaces como um recurso útil quando as classes possuem uma relação de associação simples, e não se quer uma dependência em tempo de compilação.

E se repararmos bem, as classes Raposa e Cao ficaram esquisitas, pois elas tanto obedecem a um contrato de Canideo quanto possuem uma implementação de Canideo. Se for pra ser assim, melhor herdar uma classe Canideo logo de uma vez, porque aí as subclasses recebem o contrato e a implementação de uma só vez e com código mais bonito.

Veja como fica:

abstract class Animal {
    public abstract void comer();
}

abstract class Canideo extends Animal {
    void rosnar();
    public void comer() { ... }
    public void rosnar() { ... }
    // posso implementar abstrato, ué? Por que não?
    public abstract void latir();
}

/* Um Cao é um Canideo - logo deixo isso explícito no codigo. */
class Cao extends Canideo {
    // Cadê aqueles os outros métodos que haviam aqui? Tá na classe pai!

    public void latir () { System.out.println ("au!"); }
}

/* Uma Raposa também é um Canideo. */
class Raposa extends Canideo {
    public void rosnar () { System.out.println ("rhhh!"); }
    public void latir () { System.out.println ("rau!"); }
}
1 curtida