Interfaz vs. Clase Abstracta
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy. Lección 26 de 70.
Tanto las interfaces como las clases abstractas permiten definir contratos que otras clases deben seguir, pero tienen propósitos diferentes y capacidades distintas.
Una interfaz define lo que una clase debe hacer, sin ninguna implementación. Es un contrato puro:
public interface IFlyable
{
void Fly();
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("Flapping wings");
}
}Una clase abstracta puede proporcionar tanto un contrato como una implementación compartida. Es un plano parcial:
public abstract class Vehicle
{
public string Brand { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started");
}
public abstract void Drive(); // Debe ser implementado
}
public class Car : Vehicle
{
public override void Drive()
{
Console.WriteLine("Driving on roads");
}
}Las diferencias clave se reducen a las reglas de herencia y al propósito.
Una clase puede implementar varias interfaces, pero heredar solo de una clase abstracta.
Usa una interfaz cuando clases no relacionadas necesiten compartir un comportamiento (como IFlyable para aves y aviones). Usa una clase abstracta cuando clases relacionadas compartan código y estado comunes (como vehículos que comparten un mecanismo de arranque del motor).
Las clases abstractas pueden tener constructores, campos y modificadores de acceso en sus miembros. Tradicionalmente, las interfaces no podían, aunque el C# moderno permite implementaciones predeterminadas.
Cuando tengas dudas, prefiere las interfaces por su flexibilidad; siempre puedes añadir después una clase abstracta si la implementación compartida se vuelve necesaria.
Desafío
FácilConstruyamos un sistema de reproducción multimedia que demuestre cuándo usar interfaces frente a clases abstractas. Crearás un diseño en el que dispositivos no relacionados compartan el comportamiento de reproducción mediante una interfaz, mientras que tipos de medios relacionados compartan una implementación común mediante una clase abstracta.
Organizarás tu código en cuatro archivos:
IPlayable.cs: Define una interfaz llamadaIPlayableen el espacio de nombresMedia. Esta interfaz representa el contrato para cualquier elemento que pueda reproducir medios, ya sea un teléfono, un televisor o un reproductor de música dedicado. Debe declarar un único métodoPlay()que devuelva una cadena.MediaFile.cs: Define una clase abstracta llamadaMediaFileen el espacio de nombresMedia. Esto representa la base compartida para todos los archivos multimedia. Debe tener una propiedadTitle(string) y una propiedadDuration(int, que representa segundos). Incluye un constructor que establezca ambos valores. Añade un método concreto llamadoGetInfo()que devuelva"{Title} ({Duration}s)". Declara también un método abstractoGetType()que devuelva una cadena; cada tipo de medio definirá qué tipo de archivo es.AudioFile.cs: Define una claseAudioFileen el espacio de nombresMediaque herede deMediaFilee implementeIPlayable. Añade una propiedadArtist(string). El constructor debe aceptar el título, la duración y el artista. SobrescribeGetType()para que devuelva"Audio". ImplementaPlay()para que devuelva"Playing audio: {Title} by {Artist}".Program.cs: En tu archivo principal, crea unAudioFileusando los valores de entrada. Demuestra ambos aspectos de la clase: llama aGetInfo()yGetType()(heredados de la clase abstracta) y, después, llama aPlay()(de la interfaz). Almacena el archivo de audio en una variableIPlayabley vuelve a llamar aPlay()para mostrar el polimorfismo de interfaces.
Recibirás tres entradas:
- Título de la canción
- Duración en segundos
- Nombre del artista
Imprime la salida con este formato:
Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}Por ejemplo, si las entradas son Bohemian Rhapsody, 354 y Queen, la salida debería ser:
Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by QueenObserva cómo AudioFile se beneficia de ambos enfoques: hereda una implementación compartida (GetInfo()) de la clase abstracta y, al mismo tiempo, cumple el contrato de IPlayable. La clase abstracta proporciona reutilización de código para tipos de medios relacionados, mientras que la interfaz permite que cualquier clase no relacionada sea "reproducible" sin compartir una jerarquía de herencia.
Pruébalo tú mismo
using System;
using Media;
class Program
{
public static void Main(string[] args)
{
// Lee los valores de entrada
string title = Console.ReadLine();
int duration = Convert.ToInt32(Console.ReadLine());
string artist = Console.ReadLine();
// TODO: Crea un AudioFile usando los valores de entrada
// TODO: Imprime Info: seguido del resultado de GetInfo()
// TODO: Imprime Type: seguido del resultado de GetType()
// TODO: Imprime Direct: seguido del resultado de Play()
// TODO: Almacena el archivo de audio en una variable IPlayable
// TODO: Imprime Via Interface: seguido del resultado de Play() de la variable IPlayable
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantesPractica por tu cuenta: Compilador de C# online