Pergunta

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

Deveria ser sempre uma interface se eu não quiser realmente definir uma implementação básica dos métodos?

Se eu tiver aula de Cão 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 animal por animal, mas não entendo qual usar para um animal de estimação genérico.

Foi útil?

Solução

Vamos pegar o exemplo de uma classe Dog and a 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 ambos:

public abstract class Mammal

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

  • Alimentar
  • Amigo

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

public class Dog : Mammal
public class Cat : Mammal

Agora vamos supor que existam outros mamíferos, que normalmente 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 transformar 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 gato e cachorro;colocar suas implementações em uma classe abstrata para herdar será uma má ideia.

Suas definições de cão e gato agora devem ser semelhantes a:

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

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

Conseqüentemente, porque normalmente você só pode herdar de uma classe abstrata (na maioria das linguagens OO de tipo estaticamente, isso é ...exceções incluem C++), mas é capaz de implementar múltiplas interfaces, permite construir objetos de maneira estritamente como requerido base.

Outras dicas

Bem, o próprio Josh Bloch disse em Java 2d eficaz:

Prefira interfaces a classes abstratas

Alguns pontos principais:

  • As classes existentes podem ser facilmente adaptadas para implementar um novo interface.Tudo o que você precisa fazer é adicionar os métodos necessários, se ainda não o fizerem existe e adiciona uma cláusula implements a a declaração de classe.

  • Interfaces são ideais para definir mixins.Vagamente falando, um mixin é um tipo que uma classe pode implementar, além de seu "primário type" para declarar que fornece algum comportamento opcional.Por exemplo Comparável é uma interface mista que permite que uma classe declare que sua as instâncias são ordenadas em relação a outros objetos mutuamente comparáveis.

  • Interfaces permitem a construção de tipos não hierárquicos Estruturas.As hierarquias de tipo são ótimo para organizar algumas coisas, mas outras coisas não caem perfeitamente em um hierarquia rígida.

  • As interfaces permitem melhorias de funcionalidade poderosas e seguras através do wrap- por idioma de classe.Se você usar classes abstratas para definir tipos, você deixe o programador que deseja adicionar funcionalidade sem alternativa a não ser para usar herança.

Além disso, você pode combinar as virtudes de interfaces e classes abstratas por fornecendo um esqueleto abstrato classe de implementação para acompanhar cada um 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, todas as suas implementações serão interrompidas.

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

Estilo moderno é definir IPet e PetBase.

A vantagem da interface é que outro código pode usá-la 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.

Interfaces e classes base representam duas formas diferentes de relacionamento.

Herança (classes base) representam um relacionamento "é um".Por exemplo.um cachorro ou gato "é um" animal de estimação.Este relacionamento sempre representa o (único) propósito da turma (em conjunto com o "princípio de responsabilidade única").

Interfaces, por outro lado, representam características adicionais de uma aula.Eu chamaria isso de relacionamento "é", como em "Foo é descartável", daí a IDisposable interface em C#.

Interfaces

  • Define o contrato entre 2 módulos.Não pode haver nenhuma implementação.
  • A maioria das linguagens permite implementar múltiplas interfaces
  • Modificar uma interface é uma mudança significativa.Todas as implementações precisam ser recompiladas/modificadas.
  • Todos os membros são públicos.As implementações devem ser implementadas por todos os membros.
  • Interfaces ajudam no desacoplamento.Você pode usar estruturas simuladas para simular qualquer coisa por trás de uma interface
  • Interfaces normalmente indicam um tipo de comportamento
  • Implementações de interface são desacopladas/isoladas umas das outras

Classes básicas

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

Em geral, você deve preferir interfaces a classes abstratas.Uma razão para usar uma classe abstrata é se você tiver uma implementação comum entre classes concretas.Claro, 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 o máximo de flexibilidade e portabilidade de tipos através das fronteiras.Ao passar referências entre limites, sempre passe a interface e não o tipo concreto.Isso permite que o receptor determine a implementação concreta e proporciona flexibilidade máxima.Isso é absolutamente verdade quando se programa no estilo TDD/BDD.

A Gangue dos Quatro declarou em seu livro "Como a herança expõe uma subclasse aos detalhes da implementação de seu pai, costuma-se dizer que 'a herança quebra o encapsulamento'".Eu acredito que isto seja verdade.

Isso é bastante específico do .NET, mas o livro Framework Design Guidelines argumenta que, em geral, as aulas oferecem mais flexibilidade em uma estrutura em evolução.Depois que uma interface é enviada, você não tem a chance de alterá-la sem quebrar o código que usava essa interface.Com uma classe, entretanto, você pode modificá-la e não quebrar o código vinculado a ela.Contanto que você faça as modificações corretas, o que inclui a adição de 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, conversei sobre essa diretriz com alguns desenvolvedores de nossa equipe.Muitos deles, incluindo aqueles que inicialmente discordaram das diretrizes, disseram que se arrependeram de ter enviado alguma API como interface.Não ouvi falar de nenhum caso em que alguém se arrependesse de ter enviado uma aula.

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

Juan,

Gosto de pensar nas interfaces como uma forma de caracterizar uma classe.Uma classe específica de raça de cachorro, digamos, um YorkshireTerrier, pode ser descendente da classe de cachorro pai, 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's e jogá-los na minha coleção Ocean.Portanto, agora posso acessar um conjunto específico de objetos e encontrar aqueles que atendem aos critérios que estou observando, sem inspecionar a classe muito de perto.

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

Esse pensamento vai contra a ideia 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.Eu entendo que a ideia é que realmente não custa nada fazer e agora você pode trocar e retirar 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 precisasse ser alterada, exceto que agora tenho que alterá-la em dois lugares.

Então, se você pensa como eu, com certeza diria que Gato e Cachorro são IPetáveis.É uma caracterização que combina com ambos.

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

Digamos que eu queira reunir todas as classes de Animais e colocá-las em meu contêiner Ark.

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

Eles precisam mesmo estar interligados?É suficiente saber que ambos são IPettáveis?

Muitas vezes sinto o desejo de derivar uma hierarquia de classes inteira quando na verdade preciso apenas de uma classe.Faço isso na expectativa de que algum dia possa precisar e geralmente nunca preciso.Mesmo quando o faço, geralmente descubro que preciso fazer muito para consertar.Isso porque a primeira classe que estou criando não é o Cachorro, não tenho tanta sorte, é sim 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 IPetáveis ​​(como aquele sem pelos).Agora você pode mover essa interface para todas as classes derivadas que cabem.Você descobrirá que é uma mudança muito menos significativa que, de repente, os gatos não são mais derivados do PettableBase.

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

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

saúde

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 constrói seus objetos para se comportarem de uma determinada maneira.Se você quiser alterar o comportamento, crie um novo método de fábrica (ou fábrica abstrata) que cria diferentes tipos de subobjetos.

Em alguns casos, você pode descobrir que seus objetos primários não precisam de interfaces, se todo o comportamento mutável estiver definido em objetos auxiliares.

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

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

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

Bem explicado neste Artigo do mundo Java

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

Não é incomum ter uma classe implementando 1 ou mais interfaces.

Classes abstratas que uso como base para outra coisa.

O seguinte é um extrato do artigo acima mencionado Artigo JavaWorld.com, autor Tony Sintes, 20/04/01


Interface versusclasse abstrata

A escolha de interfaces e classes abstratas não é uma questão de ou/ou.Se você precisar alterar seu design, transforme-o em uma interface.No entanto, você pode ter classes abstratas que fornecem algum comportamento padrão.Classes abstratas são excelentes candidatas dentro de estruturas de aplicativos.

As classes abstratas permitem definir alguns comportamentos;eles forçam suas subclasses a fornecer outras.Por exemplo, se você tiver uma estrutura de aplicativo, 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 à estrutura do seu aplicativo.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 por si só, 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 chegar a hora de iniciar, 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 filha.

Muitos desenvolvedores esquecem que uma classe que define um método abstrato também pode chamar esse método.Classes abstratas são uma excelente forma de criar hierarquias de herança planejadas.Eles também são uma boa opção para classes não-folha 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 extrema.Eu uso interfaces quando vejo que algo em meu design muda com frequência.

Por exemplo, o padrão Strategy permite trocar novos algoritmos e processos em seu programa sem alterar os objetos que os utilizam.Um reprodutor de mídia pode saber 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 AVI.Além disso, seu código estará repleto de instruções case inúteis.E para piorar a situação, você precisará atualizar essas declarações de caso cada vez que adicionar um novo algoritmo.Resumindo, esta não é uma forma de programar muito orientada a objetos.

Com o padrão Strategy, você pode simplesmente encapsular o algoritmo por trás de um objeto.Se você fizer isso, poderá fornecer novos plug-ins de mídia a qualquer momento.Vamos chamar a classe do plug-in MediaStrategy.Esse objeto teria um método:playStream(Streams).Portanto, para adicionar um novo algoritmo, simplesmente estendemos nossa classe de algoritmo.Agora, quando o programa encontra o novo tipo de mídia, ele simplesmente delega a reprodução do stream à nossa estratégia de mídia.Claro, você precisará de alguns detalhes para instanciar adequadamente as estratégias de algoritmo necessárias.

Este é um excelente lugar para usar uma interface.Usamos o padrão Estratégia, que indica claramente um local no design que será alterado.Assim, você deve definir a estratégia como uma interface.Geralmente, você deve favorecer as interfaces em vez da herança quando quiser que um objeto tenha um determinado tipo;neste caso, MediaStrategy.Depender da herança para identidade de tipo é perigoso;ele prende você em uma hierarquia de herança específica.Java não permite herança múltipla, portanto você não pode estender algo que forneça uma implementação útil ou mais identidade de tipo.

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

Eu tenho uma regra geral

Funcionalidade: provavelmente será diferente em todas as partes:Interface.

As partes de dados e funcionalidade serão basicamente as mesmas, e as partes diferentes: classe abstrata.

Dados e funcionalidades 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 enum.

Isso é muito grosseiro e pronto e nem um pouco estritamente definido, mas há um espectro de interfaces onde tudo deve ser alterado até enums onde tudo é corrigido um pouco como um arquivo somente leitura.

As interfaces devem ser pequenas.Muito pequeno.Se você estiver realmente decompondo 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 pronto?Se sim, então é hora de uma aula abstrata.

Classes abstratas também são limitantes.Embora eles forneçam um ótimo atalho para a produção de objetos filhos, qualquer objeto pode implementar apenas uma classe abstrata.Muitas vezes considero isso uma limitação das classes abstratas, e é por isso que utilizo muitas interfaces.

Classes abstratas podem conter diversas interfaces.Sua classe abstrata PetBase pode implementar IPet (animais de estimação têm donos) e IDigestion (animais de estimação comem, ou pelo menos deveriam).No entanto, PetBase provavelmente não implementará IMammal, uma vez que 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 estenda PetBase e adicionar IMammal.FishBase poderia ter PetBase e adicionar IFish.IFish teria ISwim e IUnderwaterBreather como interfaces.

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

O caso de Classes Base sobre Interfaces foi bem explicado nas Diretrizes de Codificação do Submain .NET:

Classes básicas vs.Interfaces Um tipo de interface é um parcial descrição de um valor, potencialmente suportado por muitos tipos de objeto.Usar classes base em vez de interfaces sempre que possível.A partir de um controle de versão perspectiva, as aulas são mais flexíveis do que interfaces.Com uma aula, você pode enviar a versão 1.0 e, em seguida, na versão 2.0 Adicione um novo método à classe.Desde que o método não seja abstrato, quaisquer classes derivadas existentes continuam para funcionar inalterado.

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

  1. Várias classes não relacionadas desejam oferecer suporte ao protocolo.
  2. Essas classes já têm classes base estabelecidas (para exemplo alguns são controles de interface do usuário (UI), e alguns são XML Web Services).
  3. A agregação não é apropriada ou praticável.Em todos os outros Situações A herança de classe é um modelo melhor.

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.

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

Ambos fornecem a reutilização de funcionalidades comuns entre tipos.A diferença mais óbvia de imediato é que as interfaces não fornecem implementação para sua funcionalidade, enquanto as classes abstratas permitem implementar algum comportamento “base” ou “padrão” e então ter a capacidade de “substituir” esse comportamento padrão pelos tipos derivados das classes, se necessário. .

Tudo isso é muito bom e proporciona uma ótima reutilização de código e segue o princípio DRY (Don’t Repeat Yourself) de desenvolvimento de software.Classes abstratas são ótimas para usar quando você tem um relacionamento “é um”.

Por exemplo:Um golden retriever “é um” tipo de cachorro.O mesmo acontece com um poodle.Ambos podem latir, como todos os cães.No entanto, você pode querer afirmar que o parque de poodles é significativamente diferente do latido de cachorro “padrão”.Portanto, pode fazer sentido implementar algo como segue:

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 fosse chamada quando qualquer um dos tipos pudesse contar apenas com o 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) gratuitamente apenas porque implementam a classe abstrata Dog.

Mas tenho certeza que você já sabia disso.

Você está aqui porque deseja entender por que deseja escolher uma interface em vez de uma classe abstrata ou vice-versa.Bem, um motivo pelo qual você pode querer escolher uma interface em vez de uma classe abstrata é quando você não tem ou deseja evitar uma implementação padrão.Isso geralmente ocorre porque os tipos que estão implementando a interface não estã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 como um humano têm “a capacidade” de nadar (visto que o humano passou nas aulas de natação no 1º ano :)).Além disso, como um pato não é um humano ou vice-versa, este 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!

Usar interfaces como o código acima permitirá que você passe um objeto para um método que “é capaz” de fazer algo.O código não se importa como faz isso… Tudo o que ele sabe é que ele pode chamar o método Swim naquele objeto e esse objeto saberá qual comportamento ocorrerá em tempo de execução com base em seu tipo.

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

Usar uma interface aqui permite que os métodos de chamada não precisem se preocupar com qual tipo ou como o comportamento é implementado.Ele apenas sabe que dada a interface, cada objeto deverá 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ê quiser 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” (ex.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 ou ter algo (ex.Pato “não é” um humano.No entanto, o pato e o humano partilham “a capacidade” de nadar).

Outra diferença a ser observada entre classes abstratas e interfaces é que uma classe pode implementar uma ou muitas interfaces, mas uma classe só pode herdar de UMA classe abstrata (ou de qualquer classe).Sim, você pode aninhar classes e ter uma hierarquia de herança (o que muitos programas fazem e deveriam ter), mas não pode herdar duas classes em uma definição de classe derivada (esta regra se aplica ao C#.Em algumas outras linguagens você consegue fazer isso, geralmente apenas devido à falta de interfaces nessas linguagens).

Lembre-se também de aderir ao Princípio de Segregação de Interface (ISP) ao usar interfaces.O ISP afirma que nenhum cliente deve ser forçado a depender de métodos que não utiliza.Por esta razão as interfaces devem ser focadas em tarefas específicas e geralmente são muito pequenas (ex.IDisposable, IComparable).

Outra dica é se você estiver desenvolvendo funcionalidades pequenas e concisas, use interfaces.Se você estiver projetando unidades funcionais grandes, use uma classe abstrata.

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

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

Uma diferença importante é que você só pode herdar um classe base, mas você pode implementar muitos interfaces.Então você só deseja usar uma classe base se estiver absolutamente certo que você não precisará herdar também uma classe base diferente.Além disso, se você achar que sua interface está ficando grande, você deve começar a dividi-la em algumas partes lógicas que definem funcionalidades independentes, já que não há regra de que sua classe não possa implementar todas elas (ou que você possa definir uma funcionalidade diferente). interface que apenas herda todos eles para agrupá-los).

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

Para desenvolver ainda mais um exemplo muito usado nesta questão específica, existem grande quantidade de coisas que são petíveis - namoradas, carros, cobertores felpudos...- então eu poderia ter uma classe Petable que fornecesse esse comportamento comum e várias classes herdadas dele.

Porém, ser petável não faz parte da natureza de nenhum desses objetos.Existem conceitos muito mais importantes que são essencial à 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 somente 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 que você entender o design orientado a objetos suficientemente melhor do que eu entendi inicialmente, você normalmente fará isso automaticamente, sem sequer pensar nisso.Portanto, a verdade nua e crua da afirmação "código 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 dizê-la - e começaria a tentar ler outros significados nela.

Outra coisa que eu acrescentaria é que se uma classe for puramente abstract - sem membros ou métodos não abstratos e não herdados expostos ao filho, pai ou cliente - então por que é uma classe?Poderia ser substituído, em alguns casos por uma interface e em outros casos por Null.

Prefira interfaces a classes abstratas

Lógica Os principais pontos a considerar [dois já mencionados aqui] são:

  • As interfaces são mais flexíveis, porque uma classe pode implementar vários Interfaces.Como o Java não tem herança múltipla, usando Classes abstratas impede que seus usuários usem qualquer outra classe hierarquia. Em geral, prefira interfaces quando não há padrão implementações ou estado. As coleções Java oferecem bons exemplos de isso (Mapa, Conjunto, etc.).
  • Classes abstratas têm a vantagem de permitir um melhor avanço compatibilidade.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 quebrando código existente. Se a compatibilidade for uma preocupação, considere usar 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].
    É claro que o assunto foi amplamente discutido em outros lugares [2,3].

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

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

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

Comentários anteriores sobre o uso de classes abstratas para implementação comum são definitivamente corretos.Um benefício que ainda não vi mencionado é que o uso de interfaces torna muito mais fácil implementar 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é que você decida que é hora de testar a coisa real).

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

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

Em resumo, se A "é um" B, então A não requer mais que B e não entrega menos que B, para cada método que expõe.

Conceitualmente, uma Interface é usada para definir formal e semiformalmente um conjunto de métodos que um objeto fornecerá.Formalmente significa um conjunto de nomes e assinaturas de métodos, 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 Application Programmer Interface), eles 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, elas podem declarar que implementam zero, uma ou mais Interfaces.Se uma classe for 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 misturam interface e implementação.Resumo fora da programação de computadores significa "resumo", ou seja, "Resumo == Interface".Uma Classe Base Abstrata pode então descrever uma interface, bem como uma implementação vazia, parcial ou completa que se destina a ser herdada.

As opiniões sobre quando usar Interfaces versus Classes Base Abstratas versus apenas Classes variam muito com base no que você está desenvolvendo e em qual linguagem você está desenvolvendo.As interfaces são frequentemente associadas apenas a linguagens de tipo estaticamente, como Java ou C#, mas linguagens de tipo dinâmico também podem ter Interfaces e Classes Base Abstratas.Em Python, por exemplo, a distinção fica clara entre uma Classe, que declara que implementos uma Interface e um objeto, que é uma instância de uma Classe, e é dito fornecer essa interface.É possível em uma linguagem dinâmica que dois objetos que são instâncias da mesma classe possam declarar que fornecem completamente diferente interfaces.Em Python isso só é possível para atributos de objetos, enquanto os métodos são estados compartilhados entre todos os objetos de uma Classe.Porém em Ruby, os objetos podem ter métodos por instância, então é possível que a Interface entre dois objetos da mesma Classe possa variar tanto quanto o programador desejar (no entanto, Ruby não possui nenhuma forma explícita de declarar Interfaces).

Em linguagens dinâmicas, a Interface para um objeto é muitas vezes assumida implicitamente, seja pela introspecção de um objeto e perguntando quais métodos ele fornece (Olhe antes de saltar) ou, de preferência, simplesmente tentando usar a Interface desejada em um objeto e capturando exceções se o objeto não fornece essa interface (mais fácil de 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 demais antecipadamente para antecipar todos os usos possíveis. do seu código.

Outra opção a ter em mente é usar a relação "tem-a", também conhecida como "é implementada 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 "is-a".

Pode não fazer tanto sentido logicamente dizer que Cão e Gato "têm" um animal de estimação, mas isso evita armadilhas comuns de herança múltipla:

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 envolvida em fazer as coisas desta forma.Mas também devemos considerar que isso ajuda a manter Cão e Gato dissociados da classe Pet (já que Cão e Gato não têm acesso aos membros privados de Pet) e deixa espaço para Cão e Gato herdarem de outra coisa. -possivelmente a classe Mamífero.

A composição é preferível quando nenhum acesso privado é necessário e você não precisa se referir a Cão e Gato usando referências/indicadores genéricos de animais de estimação.As interfaces fornecem esse recurso de referência genérica 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á-la, você se compromete a acoplar fortemente suas classes de cães e gatos à sua classe de animais de estimação, o que é um custo alto a pagar.

Entre herança, composição e interfaces não existe um caminho que seja sempre correto, e ajuda considerar como todas as três opções podem ser usadas em harmonia.Das três, a herança é normalmente a opção que deve ser usada com menos frequência.

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

A desvantagem de implementar uma classe base é a necessidade de override (ou new) métodos existentes.Isso os torna métodos virtuais, o que significa que você deve ter cuidado ao usar a instância do objeto.

Por último, a herança única do .NET me mata.Um exemplo ingênuo:Digamos que você esteja criando um controle de usuário, então você herda UserControl.Mas agora você também está impedido de herdar PetBase.Isso força você a se reorganizar, como fazer uma PetBase membro da classe, em vez disso.

@Joel:Algumas linguagens (por exemplo, C++) permitem herança múltipla.

Normalmente não implemento nenhum deles até precisar de um.Eu prefiro interfaces em vez de classes abstratas porque isso dá um pouco mais de flexibilidade.Se houver um comportamento comum em algumas das classes herdadas, eu movo isso e crio uma classe base abstrata.Não vejo necessidade de ambos, já que eles atendem essencialmente ao mesmo propósito, e ter ambos é um mau cheiro de código (imho), pois a solução foi projetada demais.

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 subclasses da pilha;e iii) apenas uma classe abstrata pode ser herdada em uma classe, enquanto múltiplas interfaces podem ser implementadas em uma classe.

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 implementam, por padrão, uma interface implícita.Também podemos definir uma interface como um papel, sempre que alguma classe precisar desempenhar esse papel, ela deverá implementá-lo dando-lhe diferentes formas de implementação dependendo da classe que o implementa.Portanto, quando você fala sobre interface, você está falando sobre polimorfismo e quando você fala sobre classe base, você está falando sobre herança.Dois conceitos de opa!!!

Descobri que um padrão Interface > Abstract > Concrete 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 ainda impõe a interface.Por exemplo:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Agora, como todos os mamíferos têm pelos e mamilos (AFAIK, não sou zoólogo), podemos colocar 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 eretos.

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 padrão apenas para programar em uma interface.Se novos métodos fossem adicionados à interface, todas as classes resultantes seriam quebradas, portanto você ainda obterá as vantagens da abordagem de interface.

Neste caso, o abstrato poderia muito bem ser concreto;entretanto, a designação abstrata ajuda a enfatizar que esse padrão está sendo empregado.

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 mantiverem o relacionamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top