Esta puede ser una pregunta genérica de OOP. Quería hacer una comparación genérica entre una interfaz y una clase abstracta en función de su uso.
¿Cuándo querría usar una interfaz y cuándo querría usar una clase abstracta ?
Esta puede ser una pregunta genérica de OOP. Quería hacer una comparación genérica entre una interfaz y una clase abstracta en función de su uso.
¿Cuándo querría usar una interfaz y cuándo querría usar una clase abstracta ?
Respuestas:
Escribí un artículo sobre eso:
Clases abstractas e interfaces
Resumiendo:
Cuando hablamos de clases abstractas, estamos definiendo características de un tipo de objeto; especificando qué es un objeto .
Cuando hablamos de una interfaz y definimos las capacidades que prometemos proporcionar, estamos hablando de establecer un contrato sobre lo que el objeto puede hacer.
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Gracias
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Una clase abstracta puede tener estado o funcionalidad compartida. Una interfaz es solo una promesa de proporcionar el estado o la funcionalidad. Una buena clase abstracta reducirá la cantidad de código que debe reescribirse porque su funcionalidad o estado pueden compartirse. La interfaz no tiene información definida para ser compartida
Personalmente, casi nunca tengo la necesidad de escribir clases abstractas.
La mayoría de las veces veo que se usan (mal) las clases abstractas, es porque el autor de la clase abstracta está usando el patrón "Método de plantilla".
El problema con el "Método de plantilla" es que casi siempre es algo reentrante: la clase "derivada" conoce no solo el método "abstracto" de su clase base que está implementando, sino también los métodos públicos de la clase base. , aunque la mayoría de las veces no es necesario llamarlos.
Ejemplo (demasiado simplificado):
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Así que aquí, el autor de esta clase ha escrito un algoritmo genérico y pretende que las personas lo usen "especializándolo" proporcionando sus propios "ganchos", en este caso, un método de "comparación".
Entonces, el uso previsto es algo como esto:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
El problema con esto es que ha unido indebidamente dos conceptos:
En el código anterior, teóricamente, el autor del método "comparar" puede volver a llamar al método "Ordenar" de la superclase ... aunque en la práctica nunca querrán o necesitarán hacer esto.
El precio que paga por este acoplamiento innecesario es que es difícil cambiar la superclase, y en la mayoría de los lenguajes OO, es imposible cambiarlo en tiempo de ejecución.
El método alternativo es utilizar el patrón de diseño "Estrategia" en su lugar:
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Así que observe ahora: todo lo que tenemos son interfaces e implementaciones concretas de esas interfaces. En la práctica, realmente no necesitas nada más para hacer un diseño OO de alto nivel.
Para "ocultar" el hecho de que hemos implementado la "clasificación de nombres" mediante el uso de una clase "QuickSort" y un "NameComparator", aún podríamos escribir un método de fábrica en alguna parte:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
Cada vez que tenga una clase abstracta puede hacer esto ... incluso cuando haya una relación natural entrante entre la clase base y la clase derivada, generalmente vale la pena hacerlas explícitas.
Una última reflexión: todo lo que hemos hecho anteriormente es "componer" una función de "Clasificación de nombres" utilizando una función de "Clasificación rápida" y una función de "Comparación de nombres" ... en un lenguaje de programación funcional, este estilo de programación se vuelve aún más natural, con menos código
Si estás viendo Java como lenguaje OOP,
"La interfaz no proporciona la implementación del método " ya no es válido con el lanzamiento de Java 8. Ahora Java proporciona implementación en la interfaz para los métodos predeterminados.
En términos simples, me gustaría usar
interfaz: para implementar un contrato por varios objetos no relacionados. Proporciona lacapacidad" TIENE A ".
clase abstracta: para implementar el mismo comportamiento o diferente entre múltiples objetos relacionados. Establece la relación " ES A ".
El sitio web de Oracle proporciona diferencias clave entre interface
y la abstract
clase.
Considere usar clases abstractas si:
Considere usar interfaces si:
Serializable
interfaz.Ejemplo:
Clase abstracta ( relación IS A )
Reader es una clase abstracta.
BufferedReader es unReader
FileReader es unReader
FileReader
y BufferedReader
se usan para un propósito común: leer datos, y están relacionados a través de la Reader
clase.
Interfaz ( capacidad HAS A )
Serializable es una interfaz.
Suponga que tiene dos clases en su aplicación, que están implementando la Serializable
interfaz
Employee implements Serializable
Game implements Serializable
Aquí no puede establecer ninguna relación a través de la Serializable
interfaz entre Employee
y Game
, que están destinados a diferentes propósitos. Ambos son capaces de serializar el estado y la comparación termina allí.
Echa un vistazo a estas publicaciones:
¿Cómo debería haber explicado la diferencia entre una interfaz y una clase abstracta?
Bien, después de haber "asimilado" esto yo mismo, aquí está en términos simples (siéntase libre de corregirme si me equivoco), sé que este tema es demasiado viejo, pero alguien más podría tropezar con él algún día ...
Las clases abstractas le permiten crear un plano, y además le permiten CONSTRUIR (implementar) propiedades y métodos que desea que TODOS sus descendientes posean.
Por otro lado, una interfaz solo le permite declarar que desea que existan propiedades y / o métodos con un nombre determinado en todas las clases que lo implementan, pero no especifica cómo debe implementarlo. Además, una clase puede implementar MUCHAS interfaces, pero solo puede extender UNA clase abstracta. Una interfaz es más una herramienta arquitectónica de alto nivel (que se vuelve más clara si comienzas a comprender patrones de diseño): un Resumen tiene un pie en ambos campos y también puede realizar parte del trabajo sucio.
¿Por qué usar uno sobre el otro? El primero permite una definición más concreta de descendientes; el segundo permite un mayor polimorfismo . Este último punto es importante para el usuario / codificador final, que puede utilizar esta información para implementar el AP I (interfaz) en una variedad de combinaciones / formas para satisfacer sus necesidades.
Creo que este fue el momento "bombilla" para mí: piense en las interfaces menos desde la perspectiva del autor y más desde la de cualquier codificador que venga más adelante en la cadena que agregue implementación a un proyecto o extienda una API.
Mis dos centavos:
Una interfaz básicamente define un contrato, que cualquier clase de implementación debe cumplir (implementar los miembros de la interfaz). No contiene ningún código.
Por otro lado, una clase abstracta puede contener código, y puede haber algunos métodos marcados como abstractos que una clase heredada debe implementar.
Las raras situaciones en las que he usado clases abstractas es cuando tengo alguna funcionalidad predeterminada de la que la clase heredadora podría no ser interesante anular, por ejemplo, una clase base abstracta, de la que heredan algunas clases especializadas.
Ejemplo (uno muy rudimentaria!): Consideremos una llamada de atención al cliente de clase base que tiene métodos abstractos como CalculatePayment()
, CalculateRewardPoints()
y algunos métodos no abstractos como GetName()
, SavePaymentDetails()
.
Especializada como clases RegularCustomer
y GoldCustomer
heredará de la Customer
clase base y poner en práctica su propio CalculatePayment()
y CalculateRewardPoints()
método de la lógica, pero re-utilizar el GetName()
y SavePaymentDetails()
métodos.
Puede agregar más funcionalidad a una clase abstracta (es decir, métodos no abstractos) sin afectar las clases secundarias que usaban una versión anterior. Mientras que agregar métodos a una interfaz afectaría a todas las clases que lo implementan, ya que ahora necesitarían implementar los miembros de la interfaz recientemente agregados.
Una clase abstracta con todos los miembros abstractos sería similar a una interfaz.
Cuándo hacer qué es algo muy simple si tienes el concepto claro en tu mente.
Las clases abstractas se pueden derivar, mientras que las interfaces se pueden implementar. Hay alguna diferencia entre los dos. Cuando deriva una clase abstracta, la relación entre la clase derivada y la clase base es una relación 'es una'. por ejemplo, un perro es un animal, una oveja es un animal, lo que significa que una clase derivada hereda algunas propiedades de la clase base.
Mientras que para la implementación de interfaces, la relación es "puede ser". Por ejemplo, un perro puede ser un perro espía. Un perro puede ser un perro de circo. Un perro puede ser un perro de raza. Lo que significa que implementa ciertos métodos para adquirir algo.
Espero estar claro.
1. Si está creando algo que proporciona una funcionalidad común a clases no relacionadas, use una interfaz.
2. Si está creando algo para objetos que están estrechamente relacionados en una jerarquía, use una clase abstracta.
Escribí un artículo sobre cuándo usar una clase abstracta y cuándo usar una interfaz. Hay mucha más diferencia entre ellos además de "un IS-A ... y un CAN-DO ...". Para mí, esas son respuestas enlatadas. Menciono algunas razones cuando usar cualquiera de ellos. Espero eso ayude.
Creo que la forma más sucinta de decirlo es la siguiente:
Propiedades compartidas => clase abstracta.
Funcionalidad compartida => interfaz.
Y para decirlo de manera menos sucinta ...
Ejemplo de clase abstracta:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
Dado que los animales tienen una propiedad compartida - número de patas en este caso - tiene sentido hacer una clase abstracta que contenga esta propiedad compartida. Esto también nos permite escribir código común que opera en esa propiedad. Por ejemplo:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
Ejemplo de interfaz:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
Tenga en cuenta aquí que Vuvuzelas y Cars son cosas completamente diferentes, pero tienen una funcionalidad compartida: hacer un sonido. Por lo tanto, una interfaz tiene sentido aquí. Además, permitirá a los programadores agrupar cosas que producen sonidos en una interfaz común, IMakeSound
en este caso. Con este diseño, podría escribir el siguiente código:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
¿Puedes decir qué produciría eso?
Por último, puedes combinar los dos.
Ejemplo combinado:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
Aquí, estamos exigiendo que todos BaseAnimal
emitan un sonido, pero aún no sabemos su implementación. En tal caso, podemos abstraer la implementación de la interfaz y delegar su implementación a sus subclases.
Un último punto, ¿recuerda cómo en el ejemplo de clase abstracta pudimos operar sobre las propiedades compartidas de diferentes objetos y en el ejemplo de interfaz pudimos invocar la funcionalidad compartida de diferentes objetos? En este último ejemplo, podríamos hacer ambas cosas.
¿Cuándo preferir una clase abstracta sobre una interfaz?
¿Cuándo preferir una interfaz sobre la clase abstracta?
Las clases pueden heredar de una sola clase base, por lo que si desea usar clases abstractas para proporcionar polimorfismo a un grupo de clases, todas deben heredar de esa clase. Las clases abstractas también pueden proporcionar miembros que ya se han implementado. Por lo tanto, puede garantizar una cierta cantidad de funcionalidad idéntica con una clase abstracta, pero no puede hacerlo con una interfaz.
Aquí hay algunas recomendaciones para ayudarlo a decidir si usar una interfaz o una clase abstracta para proporcionar polimorfismo a sus componentes.
Copiado de:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Considere usar clases abstractas si alguna de estas afirmaciones se aplica a su situación:
Considere usar interfaces si alguna de estas declaraciones se aplica a su situación:
Las respuestas varían entre idiomas. Por ejemplo, en Java una clase puede implementar (heredar de) múltiples interfaces pero solo heredar de una clase abstracta. Entonces las interfaces le dan más flexibilidad. Pero esto no es cierto en C ++.
Para mí, iría con interfaces en muchos casos. Pero prefiero las clases abstractas en algunos casos.
Las clases en OO generalmente se refieren a la implementación. Utilizo clases abstractas cuando quiero forzar algunos detalles de implementación a los niños que voy con interfaces.
Por supuesto, las clases abstractas son útiles no solo para forzar la implementación sino también para compartir algunos detalles específicos entre muchas clases relacionadas.
Utilice una clase abstracta si desea proporcionar algunas implementaciones básicas.
en java puede heredar de una clase (abstracta) para "proporcionar" funcionalidad y puede implementar muchas interfaces para "garantizar" la funcionalidad
Puramente sobre la base de la herencia, usaría un resumen en el que define relaciones abstractas claramente descendientes (es decir, animal-> gato) y / o requiere la herencia de propiedades virtuales o no públicas, especialmente el estado compartido (que las interfaces no pueden soportar )
Sin embargo, debe intentar favorecer la composición (a través de la inyección de dependencia) sobre la herencia, y tenga en cuenta que las interfaces que son contratos admiten pruebas unitarias, separación de inquietudes y herencia múltiple (variando el idioma) de una manera que los resúmenes no pueden.
Una ubicación interesante donde las interfaces funcionan mejor que las clases abstractas es cuando necesita agregar funcionalidad adicional a un grupo de objetos (relacionados o no relacionados). Si no puede darles una clase abstracta base (por ejemplo, son sealed
o ya tienen un padre), puede darles una interfaz ficticia (vacía) y luego simplemente escribir métodos de extensión para esa interfaz.
Esta puede ser una llamada muy difícil de hacer ...
Un puntero que puedo dar: un objeto puede implementar muchas interfaces, mientras que un objeto solo puede heredar una clase base (en un lenguaje OO moderno como C #, sé que C ++ tiene herencia múltiple, pero ¿no está mal visto?)
Una clase abstracta puede tener implementaciones.
Una interfaz no tiene implementaciones, simplemente define un tipo de contrato.
También puede haber algunas diferencias dependientes del lenguaje: por ejemplo, C # no tiene herencia múltiple, pero se pueden implementar múltiples interfaces en una clase.
La regla básica del pulgar es: para "sustantivos" use la clase abstracta y para "verbos" use la interfaz
Por ejemplo: car
es una clase abstracta y drive
podemos convertirla en una interfaz.
drive
en el automóvil, que es una clase abstracta.