Fuente : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # es un lenguaje maravilloso que ha madurado y evolucionado en los últimos 14 años. Esto es genial para nosotros los desarrolladores porque un lenguaje maduro nos proporciona una gran cantidad de características de lenguaje que están a nuestra disposición.
Sin embargo, con mucho poder se convierte en mucha responsabilidad. Algunas de estas funciones pueden ser mal utilizadas, o algunas veces es difícil entender por qué elegiría usar una función sobre otra. Con los años, una característica con la que he visto luchar a muchos desarrolladores es cuándo elegir usar una interfaz o elegir usar una clase abstracta. Ambos tienen sus ventajas y desventajas y el momento y lugar correctos para usar cada uno. ¿Pero cómo decidimos?
Ambos prevén la reutilización de funcionalidades comunes entre tipos. La diferencia más obvia de inmediato es que las interfaces no proporcionan implementación para su funcionalidad, mientras que las clases abstractas le permiten implementar algún comportamiento "base" o "predeterminado" y luego tienen la capacidad de "anular" este comportamiento predeterminado con los tipos derivados de clases si es necesario .
Todo esto está muy bien y proporciona una gran reutilización del código y se adhiere al principio DRY (No repetir) del desarrollo de software. Las clases abstractas son excelentes para usar cuando tienes una relación "es un".
Por ejemplo: un golden retriever "es un" tipo de perro. Entonces es un caniche. Ambos pueden ladrar, como todos los perros. Sin embargo, es posible que desee indicar que el parque de caniches es significativamente diferente de la corteza del perro "por defecto". Por lo tanto, podría tener sentido implementar algo de la siguiente manera:
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 puede ver, esta sería una excelente manera de mantener su código SECO y permitir que se llame a la implementación de la clase base cuando cualquiera de los tipos puede confiar en el Ladrido predeterminado en lugar de una implementación de caso especial. Las clases como GoldenRetriever, Boxer, Lab podrían heredar la corteza "predeterminada" (clase de bajo) sin cargo solo porque implementan la clase abstracta Dog.
Pero estoy seguro de que ya lo sabías.
Estás aquí porque quieres entender por qué quieres elegir una interfaz en lugar de una clase abstracta o viceversa. Bueno, una razón por la que es posible que desee elegir una interfaz en lugar de una clase abstracta es cuando no tiene o desea evitar una implementación predeterminada. Esto generalmente se debe a que los tipos que implementan la interfaz no están relacionados en una relación "es un". En realidad, no tienen que estar relacionados, excepto por el hecho de que cada tipo "es capaz" o tiene "la habilidad" de hacer algo o tener algo.
¿Qué diablos significa eso? Bueno, por ejemplo: un humano no es un pato ... y un pato no es un humano. Bastante obvio. Sin embargo, tanto un pato como un humano tienen "la habilidad" de nadar (dado que el humano pasó sus lecciones de natación en 1er grado :)). Además, dado que un pato no es humano o viceversa, esta no es una relación "es una", sino una relación "es capaz" y podemos usar una interfaz para ilustrar que:
// 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!
El uso de interfaces como el código anterior le permitirá pasar un objeto a un método que "pueda" hacer algo. Al código no le importa cómo lo hace ... Todo lo que sabe es que puede llamar al método Swim en ese objeto y ese objeto sabrá qué comportamiento tomar en tiempo de ejecución en función de su tipo.
Una vez más, esto ayuda a que su código permanezca SECO para que no tenga que escribir varios métodos que están llamando al objeto para preformar la misma función central (ShowHowHumanSwims (humano), ShowHowDuckSwims (pato), etc.)
El uso de una interfaz aquí permite que los métodos de llamada no tengan que preocuparse sobre qué tipo es cuál o cómo se implementa el comportamiento. Simplemente sabe que dada la interfaz, cada objeto tendrá que haber implementado el método Swim para que sea seguro llamarlo en su propio código y permitir que el comportamiento del método Swim se maneje dentro de su propia clase.
Resumen:
Por lo tanto, mi regla general es usar una clase abstracta cuando desee implementar una funcionalidad "predeterminada" para una jerarquía de clases y / o las clases o tipos con los que está trabajando comparten una relación "es una" (por ejemplo, poodle "es un "Tipo de perro).
Por otro lado, use una interfaz cuando no tenga una relación "es un" pero tenga tipos que compartan "la capacidad" de hacer o tener algo (por ejemplo, Duck "no es" un humano. Sin embargo, el pato y el humano comparten "La capacidad" de nadar).
Otra diferencia a tener en cuenta entre las clases abstractas y las interfaces es que una clase puede implementar una o varias interfaces, pero una clase solo puede heredar de UNA clase abstracta (o de cualquier clase). Sí, puede anidar clases y tener una jerarquía de herencia (que muchos programas hacen y deberían tener) pero no puede heredar dos clases en una definición de clase derivada (esta regla se aplica a C #. En algunos otros idiomas puede hacerlo, generalmente solo por la falta de interfaces en estos idiomas).
Recuerde también cuando utilice interfaces para cumplir con el Principio de segregación de interfaz (ISP). El ISP afirma que ningún cliente debería verse obligado a depender de métodos que no utiliza. Por esta razón, las interfaces deben centrarse en tareas específicas y, por lo general, son muy pequeñas (por ejemplo, IDisposable, IComparable).
Otro consejo es que si está desarrollando funcionalidades pequeñas y concisas, use interfaces. Si está diseñando unidades funcionales grandes, use una clase abstracta.
¡Espero que esto aclare las cosas para algunas personas!
Además, si puede pensar en mejores ejemplos o desea señalar algo, ¡hágalo en los comentarios a continuación!