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!