Interface versus classe base


767

Quando devo usar uma interface e quando devo usar uma classe base?

Deve sempre ser uma interface se eu não quiser realmente definir uma implementação base dos métodos?

Se eu tiver uma aula de cachorro e gato. Por que eu iria querer implementar o IPet em vez do PetBase? Eu posso entender ter interfaces para ISheds ou IBarks (IMakesNoise?), Porque elas podem ser colocadas em um animal de estimação, mas não sei qual usar para um animal de estimação genérico.


11
Apenas um ponto que acho que você deve levar em consideração - as interfaces podem representar vários limites dos quais você talvez não esteja ciente até estágios muito tardios. Por exemplo, com o .NET, você não pode serializar uma variável de membro da interface; portanto, se você tiver uma classe Zoo e uma matriz de variáveis ​​de membros de IAnimals, não poderá serializar o Zoo (e isso significa escrever WebServices ou outras coisas que exijam serialização. uma dor).
18710 synhershko

1
Esta pergunta pode ajudar a entender o conceito de interfaces. stackoverflow.com/q/8531292/1055241
gprathour

Eu só estou curioso. Eu conheci na CLR via C # trecho a seguir: I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. Não consigo entender o significado do trecho. Podemos criar alguns tipos de base e criar um tipo derivado para qualquer um deles, para que um desenvolvedor possa escolher um tipo de base. Alguém poderia explicar, por favor, o que estou perdendo? Eu acredito que isso pode fazer parte dessa pergunta. Ou devo postar outro sobre o trecho específico?
qqqqqqq 6/03

Respostas:


501

Vamos dar o seu exemplo de uma classe Dog e Cat e ilustrar usando C #:

Tanto um cachorro quanto um gato são animais, especificamente mamíferos quadrúpedes (os animais são muito gerais). Vamos supor que você tenha uma classe abstrata Mammal, para os dois:

public abstract class Mammal

Essa classe base provavelmente terá métodos padrão, como:

  • Alimentação
  • Companheiro

Todos eles são comportamentos que têm mais ou menos a mesma implementação entre as duas espécies. Para definir isso, você terá:

public class Dog : Mammal
public class Cat : Mammal

Agora vamos supor que existem outros mamíferos, os quais geralmente veremos em um zoológico:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Isso ainda será válido porque no centro da funcionalidade Feed()e Mate()ainda será o mesmo.

No entanto, girafas, rinocerontes e hipopótamos não são exatamente animais dos quais você pode criar animais de estimação. É aí que uma interface será útil:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

A implementação do contrato acima não será a mesma entre um gato e um cachorro; colocar suas implementações em uma classe abstrata para herdar será uma má idéia.

Suas definições de cachorro e gato agora devem ter a seguinte aparência:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Teoricamente, você pode substituí-los de uma classe base mais alta, mas essencialmente uma interface permite adicionar apenas o que você precisa em uma classe sem a necessidade de herança.

Conseqüentemente, como você geralmente pode herdar apenas de uma classe abstrata (na maioria das linguagens OO de tipo estatístico, que é ... as exceções incluem C ++), mas pode implementar várias interfaces, isso permite que você construa objetos estritamente conforme necessário .


149
Eu não acho tão simples assim. Você alterou ligeiramente a pergunta (requisitos) para que a interface faça mais sentido. Você deve sempre se perguntar se está definindo um contrato (interface) ou uma implementação compartilhada (classe base).
David Pokluda 11/09/09

18
A interface é um contrato. Você está apenas expondo a parte do contrato que um serviço exige. Se você tem um 'PettingZoo', certamente não quer expor o 'Mate' ao usuário!
Anthony Mastrean

10
@ David Touche, embora eu tenha feito isso para ilustrar melhor para que serve uma interface e para que serve uma classe abstrata vis-à-vis seu entendimento. Um cão e um gato não parecem ser um requisito rígido!
Jon Limjap

2
Deve-se observar que em ambientes JIT interpretados (principalmente a JVM), as chamadas de método virtual são significativamente mais rápidas que as chamadas de método de interface , dependendo do contexto. Enfatizo o "contexto" porque a JVM geralmente pode otimizar as pesquisas de método lentas. (por exemplo, se uma lista de herdeiros de interface tende a ser instâncias da mesma classe. Isso dificulta um pouco o benchmarking.) Se você está tentando otimizar algo sensível ao desempenho, só então deve considerar isso.
Philip Guin 22/02

14
Além disso, uma interface permite uma rocha de estimação, permitindo que ela seja banhada e receba truques, mas a alimentação e o acasalamento não seriam suportados, porque isso seria ridículo para uma rocha.
eclux

146

Bem, Josh Bloch disse a si mesmo no Effective Java 2d :

Preferir interfaces sobre classes abstratas

Alguns pontos principais:

  • As classes existentes podem ser facilmente adaptadas para implementar uma nova interface . Tudo o que você precisa fazer é adicionar os métodos necessários, se eles ainda não existirem, e adicionar uma cláusula implementa à declaração da classe.

  • As interfaces são ideais para definir mixins . Em termos gerais, um mixin é um tipo que uma classe pode implementar além do seu "tipo primário" para declarar que fornece algum comportamento opcional. Por exemplo, Comparable é uma interface mixin que permite que uma classe declare que suas instâncias são ordenadas em relação a outros objetos mutuamente comparáveis.

  • As interfaces permitem a construção de estruturas de tipos não hierárquicos . Hierarquias de tipos são ótimas para organizar algumas coisas, mas outras não se enquadram perfeitamente em uma hierarquia rígida.

  • As interfaces permitem aprimoramentos de funcionalidade seguros e poderosos por meio do idioma da classe de wrapper. Se você usa classes abstratas para definir tipos, deixa o programador que deseja adicionar funcionalidade sem outra alternativa a não ser usar herança.

Além disso, você pode combinar as virtudes de interfaces e classes abstratas, fornecendo uma classe de implementação esquelética abstrata para acompanhar cada interface não trivial que você exporta.

Por outro lado, as interfaces são muito difíceis de evoluir. Se você adicionar um método a uma interface, ele interromperá todas as suas implementações.

PS .: Compre o livro. É muito mais detalhado.


71
Sempre que uma alteração precisar ser feita em uma interface, a maneira ininterrupta de fazer isso é criar uma nova interface que herda da antiga. Isso preserva as implementações existentes e permite que você faça o que quiser na nova.
Scott Lawrence

5
nós temos o "Princípio de Segregação de Interface". Esse princípio nos ensina a cuidar de como escrevemos nossas interfaces. Quando escrevemos nossas interfaces, devemos adicionar apenas métodos que deveriam estar lá. Se adicionarmos métodos que não deveriam estar lá, as classes que implementam a interface também deverão implementar esses métodos. Por exemplo, se criarmos uma interface chamada Worker e adicionarmos um método para o almoço, todos os trabalhadores terão que implementá-la. E se o trabalhador for um robô? Como conclusão As interfaces que contêm métodos que não são específicos a ele são chamadas interfaces poluídas ou gordas.
Eklavyaa

Desde o Java 8, os métodos Padrão permitem adicionar novas funcionalidades às interfaces e garantir compatibilidade com versões anteriores das classes existentes que implementam essa interface. Os métodos padrão são invocados por padrão, se não forem substituídos na classe de implementação. Toda a classe implementação pode substituir métodos padrão ou eles podem chamá-los diretamente usando instance.defaultMethod ()
Arpit Rastogi

118

Interfaces e classes base representam duas formas diferentes de relacionamentos.

A herança (classes base) representa um relacionamento "é-a". Por exemplo, um cachorro ou um gato "é um" animal de estimação. Esse relacionamento sempre representa o objetivo (único) da classe (em conjunto com o "princípio de responsabilidade única" ).

As interfaces , por outro lado, representam recursos adicionais de uma classe. Eu chamaria isso de um relacionamento "é", como em " Fooé descartável", daí a IDisposableinterface em C #.


10
Fora de todas as respostas, este dá a melhor mistura de concisão, sem perda de clareza
Matt Wilko

Alguém me disse uma vez para usar uma interface quando existe uma relação "tem-a". Não tenho certeza se isso é sempre verdade; Meu laptop tem uma tela, então o laptop deve implementar o IScreen ou ter uma propriedade Screen? O último parece mais natural para mim.
Berend

1
@berend engraçado que você escreve que, porque as telas são implementadas por meio de interfaces - VGA, HDMI etc
Konstantin

Só porque é POO, temos que expandir nossa imaginação para seguir os cenários da vida real. Nem sempre se aplica.
Crismograma

110

Estilo moderno é definir IPet e PetBase.

A vantagem da interface é que outro código pode usá-lo sem qualquer vínculo com outro código executável. Completamente "limpo". Também as interfaces podem ser misturadas.

Mas as classes base são úteis para implementações simples e utilitários comuns. Portanto, forneça também uma classe base abstrata para economizar tempo e código.


coma seu bolo e coma-o também!
Darren Kopp

3
Uma interface define como outras classes podem usar seu código. Uma classe base ajuda os implementadores a implementar sua interface. Duas coisas diferentes para dois propósitos diferentes.
Bill Michell

27
Não há nada "moderno" aqui. A classe base e a interface com a mesma API são simplesmente redundantes. Você pode usar essa abordagem em alguns casos, mas não deve generalizar!
smentek

3
O comentário mais suportado para a segunda resposta suportada realmente discorda da resposta em si, interessante. Devo dizer que vejo muitos exemplos de interface e classe base coexistindo. Nesse sentido, é o caminho "moderno". Por exemplo, no padrão MVVM, existe realmente a classe ViewModelBase que implementa INotifyPropertyChanged. Mas quando o meu colega me perguntou por que eu tive a classe base, em vez de implementar a interface em cada modelo de visão, eu não sei como convencê-lo
tete

1
Corrigir. Não é uma questão de 1 ou de outra. Eles existem para resolver 2 problemas muito diferentes. Interfaces são contratos que uma classe de implementação deve abordar. Eles encontraram um favor recente (às vezes fanaticamente) pelos aspectos de IoC e TDD. As classes Abstract / Base servem para agrupar propriedades e lógica hierarquicamente comuns. Reduz o código duplicado, o que, por sua vez, aumenta a capacidade de manutenção das soluções e o torna menos propenso a erros.
ComeIn

63

Interfaces

  • Define o contrato entre 2 módulos. Não pode ter nenhuma implementação.
  • A maioria dos idiomas permite implementar várias interfaces
  • Modificar uma interface é uma mudança radical. Todas as implementações precisam ser recompiladas / modificadas.
  • Todos os membros são públicos. As implementações precisam implementar todos os membros.
  • As interfaces ajudam na dissociação. Você pode usar estruturas simuladas para simular qualquer coisa por trás de uma interface
  • As interfaces normalmente indicam um tipo de comportamento
  • As implementações de interface são dissociadas / isoladas umas das outras

Classes base

  • Permite adicionar alguns padrões implementação que você obtém gratuitamente por derivação
  • Exceto C ++, você pode derivar apenas de uma classe. Mesmo que pudesse de várias classes, geralmente é uma má ideia.
  • Mudar a classe base é relativamente fácil. Derivações não precisam fazer nada de especial
  • Classes base podem declarar funções públicas e protegidas que podem ser acessadas por derivações
  • Resumo Classes base não podem ser zombadas facilmente como interfaces
  • Classes base normalmente indicam hierarquia de tipos (IS A)
  • As derivações de classe podem depender de algum comportamento base (possuem um conhecimento complexo da implementação do pai). As coisas podem ficar confusas se você fizer uma alteração na implementação básica de um cara e quebrar os outros.

Nota: as diretrizes de design da estrutura recomendam o uso de classes base (ao contrário de interfaces) porque elas são melhoradas. Adicionar um novo método a uma classe base abstrata no vNext é uma mudança
ininterrupta

59

Em geral, você deve favorecer interfaces sobre classes abstratas. Um motivo para usar uma classe abstrata é se você tiver uma implementação comum entre classes concretas. Obviamente, você ainda deve declarar uma interface (IPet) e ter uma classe abstrata (PetBase) implementando essa interface. Usando interfaces pequenas e distintas, você pode usar múltiplos para melhorar ainda mais a flexibilidade. As interfaces permitem a quantidade máxima de flexibilidade e portabilidade de tipos através dos limites. Ao passar referências entre fronteiras, sempre passe a interface e não o tipo concreto. Isso permite que o terminal receptor determine a implementação concreta e fornece flexibilidade máxima. Isso é absolutamente verdade ao programar de forma TDD / BDD.

O Gangue dos Quatro declarou em seu livro "Como a herança expõe uma subclasse aos detalhes da implementação de seus pais, é comum dizer que 'a herança quebra o encapsulamento". Eu acredito que isto seja verdade.


Yeesh. Pessoalmente, acredito que isso é uma coisa para trás. As interfaces devem conter a funcionalidade mínima para um tipo, e as classes base devem fornecer uma estrutura rica sobre a qual a personalização pode ser construída. Coloque isso em uma interface e você dificultou a implementação.

Ninguém disse que suas interfaces precisam ser enormes. Interfaces menores e múltiplas e classes básicas mais ricas formam uma ótima API.
Kilhoffer

3
Sou apenas eu ou a maioria das classes de "trabalhador comum" compartilha uma implementação comum? Nesse contexto, isso contraria sua regra geral de favorecer interfaces. Eu reformularia sua generalização em 2 generalizações: As classes comuns que contêm pouca ou nenhuma lógica devem implementar uma interface. As classes comuns que contêm uma quantidade "decente" de lógica devem derivar de uma classe base (já que provavelmente compartilharão a funcionalidade).
Goku_da_master

@Kilhoffer "Interfaces permitem a quantidade máxima de flexibilidade e portabilidade de tipos através dos limites" por favor elabore esta declaração.
JAVA

49

Isso é bastante específico do .NET, mas o manual de diretrizes de design da estrutura argumenta que, em geral, as classes oferecem mais flexibilidade em uma estrutura em evolução. Depois que uma interface é enviada, você não pode alterá-la sem quebrar o código que usou essa interface. Com uma classe, no entanto, você pode modificá-la e não quebrar o código vinculado a ela. Desde que você faça as modificações corretas, o que inclui adicionar novas funcionalidades, você poderá estender e evoluir seu código.

Krzysztof Cwalina diz na página 81:

Ao longo das três versões do .NET Framework, falei sobre essa diretriz com alguns desenvolvedores de nossa equipe. Muitos deles, incluindo aqueles que inicialmente discordaram das diretrizes, disseram que lamentam ter enviado alguma API como interface. Não ouvi nem mesmo um caso em que alguém se arrependesse de ter enviado uma aula.

Dito isto, certamente há um lugar para interfaces. Como diretriz geral, sempre forneça uma implementação abstrata de classe base de uma interface, como se fosse um exemplo de uma maneira de implementar a interface. Na melhor das hipóteses, essa classe base economizará muito trabalho.


19

Juan,

Eu gosto de pensar em interfaces como uma maneira de caracterizar uma classe. Uma classe específica de raça de cães, por exemplo, um YorkshireTerrier, pode ser descendente da classe de cães pais, mas também implementa IFurry, IStubby e IYippieDog. Portanto, a classe define o que é a classe, mas a interface nos diz coisas sobre ela.

A vantagem disso é que me permite, por exemplo, reunir todos os IYippieDog e jogá-los na minha coleção Ocean. Portanto, agora posso alcançar um conjunto específico de objetos e encontrar objetos que atendam aos critérios que estou examinando sem inspecionar a classe muito de perto.

Acho que as interfaces realmente devem definir um subconjunto do comportamento público de uma classe. Se ele define todo o comportamento público de todas as classes que implementam, geralmente não precisa existir. Eles não me dizem nada de útil.

Esse pensamento, porém, contraria a idéia de que toda classe deve ter uma interface e você deve codificar para a interface. Tudo bem, mas você acaba com muitas interfaces individuais para as classes e isso torna as coisas confusas. Entendo que a idéia é que realmente não custa nada para fazer e agora você pode trocar e devolver as coisas com facilidade. No entanto, acho que raramente faço isso. Na maioria das vezes, estou apenas modificando a classe existente e tenho exatamente os mesmos problemas que sempre tive se a interface pública dessa classe precisar ser alterada, exceto que agora tenho que alterá-la em dois lugares.

Então, se você pensa como eu, diria que gato e cachorro são IPettable. É uma caracterização que combina com os dois.

A outra parte disso é que eles devem ter a mesma classe base? A questão é se eles precisam ser tratados de maneira ampla como a mesma coisa. Certamente, ambos são animais, mas isso se encaixa em como vamos usá-los juntos.

Digamos que eu queira reunir todas as classes de animais e colocá-las no meu contêiner de arca.

Ou eles precisam ser mamíferos? Talvez precisemos de algum tipo de fábrica de ordenha para animais?

Eles ainda precisam estar ligados? É suficiente apenas saber que ambos são IPettable?

Muitas vezes sinto o desejo de derivar toda uma hierarquia de classe quando realmente preciso apenas de uma classe. Faço isso antecipadamente um dia, talvez precise e geralmente nunca preciso. Mesmo quando o faço, geralmente acho que tenho que fazer muito para corrigi-lo. Isso porque a primeira classe que estou criando não é o Cão, não tenho tanta sorte, é o Ornitorrinco. Agora toda a minha hierarquia de classes é baseada no caso bizarro e tenho muito código desperdiçado.

Você também pode descobrir em algum momento que nem todos os gatos são IPettable (como aquele sem pêlos). Agora você pode mover essa interface para todas as classes derivadas que se encaixam. Você descobrirá que uma mudança muito menor é que, de repente, os gatos não são mais derivados do PettableBase.


18

Aqui está a definição básica e simples da interface e da classe base:

  • Classe base = herança do objeto.
  • Interface = herança funcional.

Felicidades


12

Eu recomendo usar composição em vez de herança sempre que possível. Use interfaces, mas use objetos membros para implementação básica. Dessa forma, você pode definir uma fábrica que construa seus objetos para se comportarem de uma certa maneira. Se você quiser alterar o comportamento, crie um novo método de fábrica (ou fábrica abstrata) que crie diferentes tipos de subobjetos.

Em alguns casos, você pode achar que seus objetos principais não precisam de interfaces, se todo o comportamento mutável estiver definido nos objetos auxiliares.

Portanto, em vez de IPet ou PetBase, você pode acabar com um Pet com um parâmetro IFurBehavior. O parâmetro IFurBehavior é definido pelo método CreateDog () do PetFactory. É esse parâmetro que é chamado para o método shed ().

Se você fizer isso, verá que seu código é muito mais flexível e a maioria dos seus objetos simples lida com comportamentos básicos do sistema.

Eu recomendo esse padrão mesmo em idiomas de herança múltipla.


12

É bem explicado neste artigo do Java World .

Pessoalmente, costumo usar interfaces para definir interfaces - isto é, partes do design do sistema que especificam como algo deve ser acessado.

Não é incomum que eu tenha uma classe implementando uma ou mais interfaces.

Classes abstratas que uso como base para outra coisa.

A seguir, um extrato do artigo mencionado acima, artigo JavaWorld.com, autor Tony Sintes, 20/04/01


Interface versus classe abstrata

A escolha de interfaces e classes abstratas não é uma proposição ou / ou. Se você precisar alterar seu design, faça dele uma interface. No entanto, você pode ter classes abstratas que fornecem algum comportamento padrão. As classes abstratas são excelentes candidatos dentro das estruturas de aplicativos.

As classes abstratas permitem definir alguns comportamentos; eles forçam suas subclasses a fornecer outros. Por exemplo, se você tiver uma estrutura de aplicativos, uma classe abstrata poderá fornecer serviços padrão, como manipulação de eventos e mensagens. Esses serviços permitem que seu aplicativo se conecte à sua estrutura de aplicativos. No entanto, há algumas funcionalidades específicas do aplicativo que somente seu aplicativo pode executar. Essa funcionalidade pode incluir tarefas de inicialização e desligamento, que geralmente dependem do aplicativo. Portanto, em vez de tentar definir esse comportamento, a classe base abstrata pode declarar métodos abstratos de desligamento e inicialização. A classe base sabe que precisa desses métodos, mas uma classe abstrata permite que sua classe admita que não sabe como executar essas ações; só sabe que deve iniciar as ações. Quando é hora de começar, a classe abstrata pode chamar o método de inicialização. Quando a classe base chama esse método, Java chama o método definido pela classe filho.

Muitos desenvolvedores esquecem que uma classe que define um método abstrato também pode chamar esse método. As classes abstratas são uma excelente maneira de criar hierarquias de herança planejadas. Eles também são uma boa opção para classes não-folhas em hierarquias de classes.

Classe vs. interface

Alguns dizem que você deve definir todas as classes em termos de interfaces, mas acho que a recomendação parece um pouco extremada. Uso interfaces quando vejo que algo no meu design muda frequentemente.

Por exemplo, o padrão Estratégia permite trocar novos algoritmos e processos em seu programa sem alterar os objetos que os utilizam. Um media player pode saber como reproduzir CDs, MP3s e arquivos wav. Obviamente, você não deseja codificar esses algoritmos de reprodução no player; isso tornará difícil adicionar um novo formato como o AVI. Além disso, seu código será repleto de instruções de caso inúteis. E para adicionar insulto à lesão, você precisará atualizar essas declarações de caso sempre que adicionar um novo algoritmo. Em suma, esta não é uma maneira muito orientada a objetos para programar.

Com o padrão Estratégia, você pode simplesmente encapsular o algoritmo atrás de um objeto. Se você fizer isso, poderá fornecer novos plug-ins de mídia a qualquer momento. Vamos chamar a classe de plug-in MediaStrategy. Esse objeto teria um método: playStream (Stream s). Então, para adicionar um novo algoritmo, simplesmente estendemos nossa classe de algoritmos. Agora, quando o programa encontra o novo tipo de mídia, ele simplesmente delega a reprodução do fluxo à nossa estratégia de mídia. Obviamente, você precisará de algum encanamento para instanciar adequadamente as estratégias de algoritmo necessárias.

Este é um excelente lugar para usar uma interface. Usamos o padrão de estratégia, que indica claramente um lugar no design que será alterado. Portanto, você deve definir a estratégia como uma interface. Você geralmente deve favorecer interfaces sobre herança quando quiser que um objeto tenha um determinado tipo; neste caso, MediaStrategy. Confiar na herança da identidade de tipo é perigoso; ele trava você em uma hierarquia de herança específica. O Java não permite herança múltipla; portanto, você não pode estender algo que ofereça uma implementação útil ou mais identidade de tipo.


2
+1. "Confiar na herança para a identidade de tipo é perigoso; ele trava você em uma hierarquia de herança específica". Essa frase descreve perfeitamente minhas razões para preferir interfaces.
Engenheiro de

Além disso, em vez de estender, componha a implementação por trás de cada método da sua interface.
Engenheiro de

10

Lembre-se também de não se deixar levar pelo OO ( consulte o blog ) e sempre modele objetos com base no comportamento necessário, se você estava projetando um aplicativo em que o único comportamento necessário era um nome genérico e uma espécie para um animal, você só precisaria uma classe Animal com uma propriedade para o nome, em vez de milhões de classes para todos os animais possíveis no mundo.


10

Eu tenho uma regra geral

Funcionalidade: provável que seja diferente em todas as partes: Interface.

Dados e funcionalidade, partes serão basicamente os mesmos, partes diferentes: classe abstrata.

Dados e funcionalidade, realmente funcionando, se estendidos apenas com pequenas alterações: classe comum (concreta)

Dados e funcionalidade, sem alterações planejadas: classe comum (concreta) com modificador final.

Dados e talvez funcionalidade: somente leitura: membros da enumeração.

Isso é muito difícil e pronto e não é estritamente definido, mas existe um espectro de interfaces em que tudo deve ser alterado para enumerações em que tudo é corrigido um pouco como um arquivo somente leitura.


7

As interfaces devem ser pequenas. Muito pequeno. Se você realmente está dividindo seus objetos, suas interfaces provavelmente conterão apenas alguns métodos e propriedades muito específicos.

Classes abstratas são atalhos. Existem coisas que todos os derivados do PetBase compartilham que você pode codificar uma vez e terminar? Se sim, é hora de uma aula abstrata.

Classes abstratas também são limitantes. Embora eles ofereçam um ótimo atalho para a produção de objetos filho, qualquer objeto específico pode implementar apenas uma classe abstrata. Muitas vezes, acho isso uma limitação das classes Abstratas, e é por isso que uso muitas interfaces.

Classes abstratas podem conter várias interfaces. Sua classe abstrata do PetBase pode implementar IPet (animais de estimação têm proprietários) e IDigestion (animais de estimação comem, ou pelo menos deveriam). No entanto, o PetBase provavelmente não implementará o IMammal, pois nem todos os animais de estimação são mamíferos e nem todos os mamíferos são animais de estimação. Você pode adicionar um MammalPetBase que estende o PetBase e adicionar IMammal. O FishBase pode ter o PetBase e adicionar o IFish. O IFish teria ISwim e IUnderwaterBreather como interfaces.

Sim, meu exemplo é muito complicado para o exemplo simples, mas isso faz parte da grande parte de como interfaces e classes abstratas funcionam juntas.


7

Fonte : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C # é uma linguagem maravilhosa que amadureceu e evoluiu nos últimos 14 anos. Isso é ótimo para nós, desenvolvedores, porque uma linguagem madura nos fornece uma infinidade de recursos de linguagem que estão à nossa disposição.

No entanto, com muito poder se torna muita responsabilidade. Alguns desses recursos podem ser mal utilizados ou, às vezes, é difícil entender por que você escolheria usar um recurso em detrimento de outro. Ao longo dos anos, um recurso com o qual eu tenho visto muitos desenvolvedores se debater é quando optar por usar uma interface ou usar uma classe abstrata. Ambos têm vantagens e desvantagens e a hora e o local corretos para usar cada um. Mas como nós decidimos ???

Ambos fornecem a reutilização de funcionalidades comuns entre tipos. A diferença mais óbvia imediatamente é que as interfaces não fornecem implementação para sua funcionalidade, enquanto as classes abstratas permitem implementar algum comportamento "básico" ou "padrão" e, em seguida, têm a capacidade de "substituir" esse comportamento padrão pelos tipos derivados de classes, se necessário .

Tudo está bem e fornece uma ótima reutilização de código e segue o princípio de desenvolvimento de software DRY (Não se repita). As aulas abstratas são ótimas para usar quando você tem um relacionamento "é um".

Por exemplo: Um golden retriever "é um" tipo de cachorro. Então é um poodle. Ambos podem latir, como todos os cães. No entanto, você pode indicar que o parque de poodle é significativamente diferente do latido de cachorro "padrão". Portanto, pode fazer sentido implementar algo da seguinte maneira:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Como você pode ver, essa seria uma ótima maneira de manter seu código DRY e permitir que a implementação da classe base seja chamada quando qualquer um dos tipos puder confiar apenas no Bark padrão em vez de uma implementação de caso especial. As classes como GoldenRetriever, Boxer, Lab poderiam herdar o Bark "padrão" (classe de baixo) sem nenhum custo, apenas porque implementam a classe abstrata Dog.

Mas tenho certeza que você já sabia disso.

Você está aqui porque deseja entender por que pode escolher uma interface em vez de uma classe abstrata ou vice-versa. Bem, um motivo pelo qual você pode escolher uma interface em vez de uma classe abstrata é quando você não possui ou deseja impedir uma implementação padrão. Isso geralmente ocorre porque os tipos que estão implementando a interface não relacionados em um relacionamento "é um". Na verdade, eles não precisam estar relacionados, exceto pelo fato de que cada tipo "é capaz" ou tem "a capacidade" de fazer algo ou ter algo.

Agora, o que diabos isso significa? Bem, por exemplo: um humano não é um pato ... e um pato não é um humano. Muito obvio. No entanto, tanto um pato quanto um humano têm "a capacidade" de nadar (já que o humano passou nas aulas de natação na 1ª série :)). Além disso, como um pato não é humano ou vice-versa, isso não é um relacionamento "é um", mas sim um relacionamento "capaz" e podemos usar uma interface para ilustrar isso:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

O uso de interfaces como o código acima permitirá que você passe um objeto para um método que "é capaz" de fazer alguma coisa. O código não se importa com o que faz ... Tudo o que sabe é que ele pode chamar o método Swim nesse objeto e esse objeto saberá qual comportamento é executado no tempo de execução com base em seu tipo.

Mais uma vez, isso ajuda seu código a ficar SECO para que você não precise escrever vários métodos que estão chamando o objeto para executar a mesma função principal (ShowHowHumanSwims (humano), ShowHowDuckSwims (duck), etc.)

O uso de uma interface aqui permite que os métodos de chamada não precisem se preocupar com que tipo é qual ou como o comportamento é implementado. Apenas sabe que, dada a interface, cada objeto precisará ter implementado o método Swim, para que seja seguro chamá-lo em seu próprio código e permitir que o comportamento do método Swim seja tratado dentro de sua própria classe.

Resumo:

Portanto, minha regra principal é usar uma classe abstrata quando você deseja implementar uma funcionalidade "padrão" para uma hierarquia de classes ou / e as classes ou tipos com os quais você está trabalhando compartilham um relacionamento "é um" (por exemplo, poodle "é um ”Tipo de cachorro).

Por outro lado, use uma interface quando você não tem um relacionamento "é um", mas tem tipos que compartilham "a capacidade" de fazer algo ou ter algo (por exemplo, Duck "não é" um ser humano. No entanto, Duck e Human compartilham “A capacidade” de nadar).

Outra diferença a ser observada entre as classes abstratas e as interfaces é que uma classe pode implementar uma a várias interfaces, mas uma classe só pode herdar de UMA classe abstrata (ou qualquer outra classe). Sim, você pode aninhar classes e ter uma hierarquia de herança (que muitos programas fazem e deveriam ter), mas não é possível herdar duas classes em uma definição de classe derivada (essa regra se aplica ao C #. Em outros idiomas, você pode fazer isso, geralmente apenas por falta de interfaces nessas linguagens).

Lembre-se também ao usar interfaces para aderir ao Princípio de Segregação de Interface (ISP). O ISP declara que nenhum cliente deve ser forçado a depender dos métodos que não usa. Por esse motivo, as interfaces devem se concentrar em tarefas específicas e geralmente são muito pequenas (por exemplo, IDisposable, IComparable).

Outra dica é se você estiver desenvolvendo pequenos e concisos bits de funcionalidade, use interfaces. Se você estiver projetando grandes unidades funcionais, use uma classe abstrata.

Espero que isso esclareça as coisas para algumas pessoas!

Além disso, se você puder pensar em exemplos melhores ou quiser apontar algo, faça-o nos comentários abaixo!


6

O caso das Classes base sobre interfaces foi explicado bem nas Diretrizes de codificação do submain .NET:

Classes Base vs. Interfaces Um tipo de interface é uma descrição parcial de um valor, potencialmente suportado por muitos tipos de objetos. Use classes base em vez de interfaces sempre que possível. De uma perspectiva de versão, as classes são mais flexíveis que as interfaces. Com uma classe, você pode enviar a Versão 1.0 e, em seguida, na Versão 2.0, adicionar um novo método à classe. Desde que o método não seja abstrato, qualquer classe derivada existente continuará a funcionar inalterada.

Como as interfaces não suportam a herança de implementação, o padrão que se aplica às classes não se aplica às interfaces. Adicionar um método a uma interface é equivalente a adicionar um método abstrato a uma classe base; qualquer classe que implemente a interface será interrompida porque a classe não implementa o novo método. As interfaces são apropriadas nas seguintes situações:

  1. Várias classes não relacionadas desejam dar suporte ao protocolo.
  2. Essas classes já estabeleceram classes base (por exemplo, algumas são controles da interface do usuário) e outras são serviços da Web XML.
  3. A agregação não é apropriada ou praticável. Em todas as outras situações, a herança de classe é um modelo melhor.

Eu sinto que esta resposta deve receber mais atenção. Isso vai de encontro a muitas respostas aqui. Eu não diria que concordo completamente, mas há ótimos pontos aqui.
kayleeFrye_onDeck

5

Uma diferença importante é que você pode herdar apenas uma classe base, mas pode implementar muitas interfaces. Portanto, você só deseja usar uma classe base se tiver certeza absoluta de que não precisará herdar também uma classe base diferente. Além disso, se você achar que sua interface está ficando grande, comece a dividi-la em algumas partes lógicas que definem a funcionalidade independente, já que não há regra de que sua classe não possa implementar todas (ou você pode definir uma configuração diferente). interface que apenas herda todos eles para agrupá-los).


4

Quando comecei a aprender sobre programação orientada a objetos, cometi o erro fácil e provavelmente comum de usar a herança para compartilhar comportamentos comuns - mesmo quando esse comportamento não era essencial à natureza do objeto.

Para desenvolver ainda mais um exemplo muito usado nessa questão em particular, há muitas coisas que podem ser usadas - namoradas, carros, cobertores felpudos ... - então eu poderia ter uma classe Petable que forneceu esse comportamento comum e várias classes herdadas a partir dele.

No entanto, ser petável não faz parte da natureza de nenhum desses objetos. Existem conceitos muito mais importantes que são essenciais à sua natureza - a namorada é uma pessoa, o carro é um veículo terrestre, o gato é um mamífero ...

Os comportamentos devem ser atribuídos primeiro às interfaces (incluindo a interface padrão da classe) e promovidos a uma classe base apenas se forem (a) comuns a um grande grupo de classes que são subconjuntos de uma classe maior - no mesmo sentido que "gato" e "pessoa" são subconjuntos de "mamífero".

O problema é que, depois de entender o design orientado a objetos suficientemente melhor do que eu entendi no início, você normalmente faz isso automaticamente, sem sequer pensar nisso. Portanto, a pura verdade da afirmação "codificar para uma interface, não para uma classe abstrata" torna-se tão óbvia que é difícil acreditar que alguém se daria ao trabalho de dizer isso - e começou a tentar ler outros significados.

Outra coisa que eu acrescentaria é que, se uma classe é puramente abstrata - sem membros ou métodos não abstratos e não herdados expostos a filhos, pais ou clientes -, por que é uma classe? Pode ser substituído, em alguns casos por uma interface e em outros casos por Null.


Uma classe puramente abstrata pode fornecer comportamentos padrão para métodos. Isso é útil quando todas as suas classes concretas compartilham métodos comuns que seriam redundantes para reimplementar repetidamente.
Adam Hughes

4

Preferir interfaces sobre classes abstratas

Justificativa, os principais pontos a serem considerados [dois já mencionados aqui] são:

  • As interfaces são mais flexíveis, porque uma classe pode implementar várias interfaces. Como o Java não possui herança múltipla, o uso de classes abstratas impede que seus usuários usem qualquer outra hierarquia de classes. Em geral, prefira interfaces quando não houver implementações ou estado padrão. As coleções Java oferecem bons exemplos disso (Mapa, Conjunto, etc.).
  • As classes abstratas têm a vantagem de permitir uma melhor compatibilidade direta. Depois que os clientes usam uma interface, você não pode alterá-la; se eles usarem uma classe abstrata, você ainda poderá adicionar comportamento sem quebrar o código existente. Se a compatibilidade for uma preocupação, considere o uso de classes abstratas.
  • Mesmo se você tiver implementações padrão ou estado interno, considere oferecer uma interface e uma implementação abstrata dela . Isso ajudará os clientes, mas ainda lhes permitirá maior liberdade, se desejado [1].
    Obviamente, o assunto já foi discutido detalhadamente em outros lugares [2,3].

[1] Ele adiciona mais código, é claro, mas se a brevidade é sua principal preocupação, você provavelmente deveria ter evitado o Java em primeiro lugar!

[2] Joshua Bloch, Java efetivo, itens 16-18.

[3] http://www.codeproject.com/KB/ar ...


3

Comentários anteriores sobre o uso de classes abstratas para implementação comum estão definitivamente em jogo. Um benefício que ainda não vi mencionado é que o uso de interfaces facilita muito a implementação de objetos simulados para fins de teste de unidade. Definir IPet e PetBase como Jason Cohen descreveu permite simular facilmente diferentes condições de dados, sem a sobrecarga de um banco de dados físico (até você decidir que é hora de testar a coisa real).


3

Não use uma classe base, a menos que você saiba o que significa e se aplica nesse caso. Se aplicável, use-o; caso contrário, use interfaces. Mas observe a resposta sobre pequenas interfaces.

A herança pública é usada em excesso no OOD e expressa muito mais do que a maioria dos desenvolvedores percebe ou está disposta a cumprir. Veja o princípio da substituibilidade de Liskov

Em suma, se A "é um" B, então A requer não mais que B e entrega não menos que B, para todo método exposto.


3

Outra opção a ter em mente é usar o relacionamento "tem-a", também conhecido como "é implementado em termos de" ou "composição". Às vezes, essa é uma maneira mais limpa e flexível de estruturar as coisas do que usar a herança "é-a".

Pode não fazer tanto sentido logicamente dizer que cachorro e gato "têm" um animal de estimação, mas evita várias armadilhas comuns de herança:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Sim, este exemplo mostra que há muita duplicação de código e falta de elegância para fazer as coisas dessa maneira. Mas também se deve considerar que isso ajuda a manter o cão e o gato separados da classe Pet (na medida em que o cão e o gato não têm acesso aos membros particulares do Pet) e deixa espaço para o cão e o gato herdarem de outra coisa. Possivelmente a classe Mammal.

A composição é preferível quando não é necessário acesso privado e você não precisa se referir a Cães e gatos usando referências / indicadores genéricos para animais de estimação. As interfaces fornecem esse recurso de referência genérico e podem ajudar a reduzir a verbosidade do seu código, mas também podem ofuscar as coisas quando estão mal organizadas. A herança é útil quando você precisa de acesso de membro privado e, ao usá-lo, está se comprometendo a acoplar altamente suas classes Cão e Gato à sua classe Pet, o que é um alto custo a pagar.

Entre herança, composição e interfaces, não existe uma maneira sempre certa e ajuda a considerar como as três opções podem ser usadas em harmonia. Dos três, a herança geralmente é a opção que deve ser usada com menos frequência.


3

Conceitualmente, uma interface é usada para definir formal e semi-formalmente um conjunto de métodos que um objeto fornecerá. Formalmente significa um conjunto de nomes e assinaturas de métodos e semi-formalmente significa documentação legível por humanos associada a esses métodos.

Interfaces são apenas descrições de uma API (afinal, API significa interface de programação de aplicativos ), não podem conter nenhuma implementação e não é possível usar ou executar uma interface. Eles apenas explicitam o contrato de como você deve interagir com um objeto.

As classes fornecem uma implementação e podem declarar que implementam zero, uma ou mais interfaces. Se uma classe deve ser herdada, a convenção é prefixar o nome da classe com "Base".

Há uma distinção entre uma classe base e uma classe base abstrata (ABC). ABCs combinam interface e implementação. Resumo fora da programação do computador significa "resumo", ou seja, "resumo == interface". A classe base abstrata pode descrever uma interface, bem como uma implementação vazia, parcial ou completa que deve ser herdada.

As opiniões sobre quando usar interfaces versus classes básicas abstratas versus apenas classes variam muito, com base no que você está desenvolvendo e em qual idioma você está desenvolvendo. As interfaces geralmente são associadas apenas a linguagens estaticamente tipadas como Java ou C #, mas linguagens dinamicamente tipadas também podem ter interfaces e classes base abstratas . Em Python, por exemplo, a distinção é esclarecida entre uma Classe, que declara que implementa uma interface , e um objeto, que é uma instância de uma classe , e é dito que fornece essa interface. Em uma linguagem dinâmica, é possível que dois objetos que são ambas instâncias da mesma classe possam declarar que fornecem interfaces completamente diferentes . No Python, isso só é possível para atributos de objeto, enquanto métodos são estado compartilhado entre todos os objetos de uma classe . No entanto, no Ruby, os objetos podem ter métodos por instância, portanto, é possível que a interface entre dois objetos do mesmo classe possa variar tanto quanto o programador desejar (no entanto, o Ruby não tem nenhuma maneira explícita de declarar interfaces).

Em linguagens dinâmicas, a interface para um objeto geralmente é assumida implicitamente, seja pela introspecção de um objeto e perguntando quais métodos ele fornece ( observe antes de pular ) ou, de preferência, simplesmente tentando usar a interface desejada em um objeto e capturando exceções se o objeto não fornece essa interfacemais fácil pedir perdão do que permissão ). Isso pode levar a "falsos positivos", onde duas interfaces têm o mesmo nome de método, mas são semanticamente diferentes. No entanto, a desvantagem é que seu código é mais flexível, pois você não precisa especificar muito de antemão para antecipar todos os usos possíveis do seu código.


2

Depende de suas necessidades. Se o IPet for simples o suficiente, eu preferiria implementar isso. Caso contrário, se o PetBase implementar uma tonelada de funcionalidades que você não deseja duplicar, faça isso.

A desvantagem de implementar uma classe base é o requisito para override(ou new) métodos existentes. Isso os torna métodos virtuais, o que significa que você deve ter cuidado com o uso da instância do objeto.

Por fim, a herança única do .NET me mata. Um exemplo ingênuo: digamos que você está fazendo um controle de usuário e herda UserControl. Mas agora você está impedido de herdar também PetBase. Isso obriga a reorganizar, como tornar um PetBasemembro da classe.


2

Normalmente, eu também não implemento até precisar de um. Eu sou a favor das interfaces em detrimento das classes abstratas, porque isso oferece um pouco mais de flexibilidade. Se houver um comportamento comum em algumas das classes herdadas, eu subo e faço uma classe base abstrata. Não vejo a necessidade de ambos, já que eles essencialmente servem o mesmo objetivo, e ter ambos é um mau cheiro (imho) de código que a solução foi projetada em excesso.


2

Em relação ao C #, em alguns sentidos, interfaces e classes abstratas podem ser intercambiáveis. No entanto, as diferenças são: i) interfaces não podem implementar código; ii) por causa disso, as interfaces não podem chamar mais a pilha para a subclasse; e iii) somente a classe abstrata pode ser herdada em uma classe, enquanto várias interfaces podem ser implementadas em uma classe.


2

Por definição, a interface fornece uma camada para se comunicar com outro código. Todas as propriedades e métodos públicos de uma classe são, por padrão, implementando uma interface implícita. Também podemos definir uma interface como uma função, sempre que qualquer classe precisar desempenhar essa função, ela deve implementá-la, oferecendo-lhe diferentes formas de implementação, dependendo da classe que a implementa. Portanto, quando você fala sobre interface, você está falando sobre polimorfismo e quando você está falando sobre classe base, você está falando sobre herança. Dois conceitos de oops !!!


2

Descobri que um padrão de Interface> Resumo> Concreto funciona no seguinte caso de uso:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

A classe abstrata define atributos compartilhados padrão das classes concretas, mas reforça a interface. Por exemplo:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Agora, como todos os mamíferos têm cabelos e mamilos (AFAIK, eu não sou zoólogo), podemos incluir isso na classe base abstrata

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

E então as classes concretas apenas definem que andam de pé.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Esse design é bom quando há muitas classes concretas e você não deseja manter o clichê apenas para programar em uma interface. Se novos métodos fossem adicionados à interface, isso quebraria todas as classes resultantes, portanto você ainda obterá as vantagens da abordagem da interface.

Nesse caso, o resumo poderia muito bem ser concreto; no entanto, a designação abstrata ajuda a enfatizar que esse padrão está sendo empregado.


1

Um herdeiro de uma classe base deve ter um relacionamento "é um". Interface representa Um relacionamento "implementa um". Portanto, use apenas uma classe base quando seus herdeiros manterem o relacionamento.


1

Use Interfaces para impor um contrato às famílias de classes não relacionadas. Por exemplo, você pode ter métodos de acesso comuns para classes que representam coleções, mas contém dados radicalmente diferentes, ou seja, uma classe pode representar um conjunto de resultados de uma consulta, enquanto a outra pode representar as imagens em uma galeria. Além disso, você pode implementar várias interfaces, permitindo combinar (e significar) os recursos da classe.

Use Herança quando as classes tiverem um relacionamento comum e, portanto, possuam uma assinatura estrutural e comportamental semelhante, ou seja, Carro, Moto, Caminhão e SUV são todos os tipos de veículos rodoviários que podem conter várias rodas, uma velocidade máxima

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.