¿Cómo decidir entre una interfaz o una clase base para una nueva implementación?

Cuando se trata de la implementación, ¿cómo debo decidir elegir un tipo base o una interfaz? Intenté trabajar en algunos ejemplos, pero no tengo la idea completa 🙁

Ejemplos sobre cómo y por qué serían muy apreciados.

Una clase base, abstracta o no, puede contener miembros implementados. Una interfaz no puede. Si todas sus implementaciones van a tener un desempeño similar, una clase base podría ser el camino a seguir porque todas sus clases secundarias pueden compartir las mismas implementaciones de los miembros en la clase base. Si no van a compartir implementaciones, entonces una interfaz podría ser el camino a seguir.

Ejemplo:

class Person { string Name { get; set; } } class Employee : Person { string Company { get; set; } } 

Tiene sentido que el Empleado herede de la Persona porque la clase Empleado no tiene que definir una propiedad de Name porque comparte la implementación.

 interface IPolygon { double CalculateArea() } class Rectangle : IPolygon { double Width { get; set; } double Height { get; set; } double CalculateArea() { return this.Width * this.Height; } } class Triangle : IPolygon { double Base { get; set; } double Height { get; set; } double CalculateArea() { return 0.5 * this.Base * this.Height; } } 

Debido a que Rectangle y Triangle tienen implementaciones tan diferentes de CalculateArea , no tiene sentido heredarlas de una clase base.

Si crea una clase base y encuentra que solo contiene miembros abstractos, también puede usar una interfaz.

Y, como dice j__m, no puede heredar de varias clases base, pero puede implementar múltiples interfaces.

Por lo general, primero defino las interfaces, y si me encuentro duplicando código en mis implementaciones, creo una clase base que implementa la interfaz y hago que mis implementaciones hereden de ella.

Para decidir si usar una clase abstracta o una interfaz, encuentro este artículo muy útil. Fuente :

Una buena manera de distinguir entre un caso para uno u otro para mí siempre ha sido la siguiente:

  1. ¿Hay muchas clases que se pueden “agrupar” y describir por un nombre ? Si es así, tenga una clase abstracta por el nombre de este sustantivo y herede las clases de ella. (Un factor decisivo es que estas clases comparten la funcionalidad, y nunca crearías una instancia solo de un Animal … siempre harías una instancia de un cierto tipo de Animal : una implementación de tu clase base de Animal ). clase Animal , y esta clase base abstracta implementará un método void Breathe () que todos los animales harán exactamente de la misma manera. (Podría hacer que este método sea virtual para que pueda anularlo para ciertos animales, como Fish, que no respira lo mismo que la mayoría de los animales).

  2. ¿Qué tipos de verbos se pueden aplicar a mi clase, que en general también podrían aplicarse a otros? Crea una interfaz para cada uno de estos verbos. Ejemplo : Todos los animales pueden ser alimentados, así que crearé una interfaz llamada IFeedable y haré que Animal implemente eso. Sin embargo, solo el perro y el caballo son lo suficientemente agradables como para implementar ILikeable . No lo implementaré en la clase base, ya que esto no se aplica a Cat .

Por favor, mire también esta pregunta de clase Interfaz vs Base .

Una de las razones para ir con las clases abstractas es cuando tenemos que hacer cumplir alguna inicialización (como los estados a través del constructor).

La interfaz no le permite definir los contratos del constructor.

En el siguiente ejemplo, cada objeto Animal DEBE tener un NOMBRE. Esto no se puede hacer cumplir a través de una interfaz.

 public abstract class Animal { public Animal(string name) { this.Name = name; } public string Name { get; private set; } } public class Cat : Animal { public Cat(string name) : base(name) { } string NoOfLegs { get; set; } } class Program { static void Main(string[] args) { Animal aCat = new Cat("a"); } } 

En realidad no son necesariamente mutuamente excluyentes. Puede utilizar ambos dependiendo de cómo evolucione la implementación de su código.

Una interfaz suele ser la enunciación de un contrato. Define un comportamiento esperado, que los implementadores deben honrar. Y generalmente se considera una buena práctica codificar su API pública contra las interfaces . De esa manera, reduce el acoplamiento a los detalles de sus implementaciones y permite una refactorización y un mantenimiento más fáciles de su código. Ahora lo que califica como api público, son los componentes de software que incorporan la interacción de alto nivel definida en su diseño y están destinados a ser reutilizables por usted mismo y por otros dentro del mismo proyecto o dentro de varios proyectos de scope independiente.

Una clase base ya es parte de la implementación . Si implementa una interfaz o no. Y vincula su jerarquía potencial con detalles de implementaciones específicas: estado de objeto, métodos reemplazables o no reemplazables, etc.

Las interfaces son una construcción más flexible. Solo puede tener una clase base, pero puede implementar muchas interfaces. Si necesita un objeto para admitir múltiples comportamientos, pero más de uno de ellos requiere una clase base específica, entonces no podrá hacerlo.

Como Dan señala, las clases base tienen una ventaja de conveniencia en muchos idiomas, ya que puede proporcionar una implementación base. Para hacer esto con una interfaz se requiere que cree una clase que proporcione la implementación base y luego delegue manualmente su implementación de cada método de interfaz a esa clase, no tan conveniente.

Encuentro una analogía que ayuda aquí:

Clase base abstracta: – Un fabricante de automóviles puede desarrollar un motor de gasolina que tiene algunas variantes (1.6, 2L, etc.). La fundición del bloque del motor podría considerarse la clase base abstracta , es decir, define la forma básica y las características del motor. Una versión 2L puede tener una culata más grande, etc.

Interfaces: – El motor también puede usar varios componentes estándar, por ejemplo, alternador, radiador, motor de arranque, etc., por lo que tiene que implementar interfaces definidas por estos componentes. Estos componentes fueron diseñados típicamente sin el conocimiento de los motores que pueden usarlos.