Menu
Coddy logo textTech

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 sirven para propósitos diferentes y tienen 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 el propósito.

Una clase puede implementar múltiples interfaces pero heredar de solo 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 de motor).

Las clases abstractas pueden tener constructores, campos y modificadores de acceso en sus miembros. Las interfaces tradicionalmente no podían, aunque el C# moderno permite implementaciones predeterminadas.

En caso de duda, prefiere las interfaces por su flexibilidad; siempre puedes añadir una clase abstracta más tarde si una implementación compartida resulta necesaria.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de reproductor de medios que demuestre cuándo usar interfaces frente a clases abstractas. Crearás un diseño donde dispositivos no relacionados comparten el comportamiento de reproducción a través de una interfaz, mientras que los tipos de medios relacionados comparten una implementación común a través de una clase abstracta.

Organizarás tu código en cuatro archivos:

  • IPlayable.cs: Define una interfaz llamada IPlayable en el espacio de nombres Media. Esta interfaz representa el contrato para cualquier cosa que pueda reproducir medios, ya sea un teléfono, una televisión o un reproductor de música dedicado. Debe declarar un único método Play() que devuelva un string.
  • MediaFile.cs: Define una clase abstracta llamada MediaFile en el espacio de nombres Media. Esta representa la base compartida para todos los archivos de medios. Debe tener una propiedad Title (string) y una propiedad Duration (int, que representa segundos). Incluye un constructor que establezca ambos valores. Añade un método concreto llamado GetInfo() que devuelva "{Title} ({Duration}s)". También declara un método abstracto GetType() que devuelva un string; cada tipo de medio definirá qué tipo de archivo es.
  • AudioFile.cs: Define una clase AudioFile en el espacio de nombres Media que herede de MediaFile e implemente IPlayable. Añade una propiedad Artist (string). El constructor debe aceptar el título, la duración y el artista. Sobrescribe GetType() para que devuelva "Audio". Implementa Play() para que devuelva "Playing audio: {Title} by {Artist}".
  • Program.cs: En tu archivo principal, crea un AudioFile usando los valores de entrada. Demuestra ambos aspectos de la clase: llama a GetInfo() y GetType() (heredados de la clase abstracta), luego llama a Play() (de la interfaz). Almacena el archivo de audio en una variable de tipo IPlayable y llama a Play() de nuevo para mostrar el polimorfismo de interfaz.

Recibirás tres entradas:

  • Título de la canción
  • Duración en segundos
  • Nombre del artista

Imprime la salida en 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 Queen

Observa cómo AudioFile se beneficia de ambos enfoques: hereda la implementación compartida (GetInfo()) de la clase abstracta y, al mismo tiempo, cumple con el contrato 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.

Hoja de referencia

Una interfaz define un contrato que las clases deben seguir, sin ninguna implementación:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

Una clase abstracta proporciona tanto un contrato como una implementación compartida:

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");
    }
}

Diferencias clave:

  • Una clase puede implementar múltiples interfaces pero heredar de una sola clase abstracta
  • Use interfaces cuando clases no relacionadas necesiten compartir comportamiento
  • Use clases abstractas cuando clases relacionadas compartan código y estado comunes
  • Las clases abstractas pueden tener constructores, campos y modificadores de acceso
  • Prefiera las interfaces para obtener flexibilidad

Pruébalo tú mismo

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // Leer los valores de entrada
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: Crear un AudioFile usando los valores de entrada
        
        // TODO: Imprimir Info: seguido del resultado de GetInfo()
        
        // TODO: Imprimir Type: seguido del resultado de GetType()
        
        // TODO: Imprimir Direct: seguido del resultado de Play()
        
        // TODO: Almacenar el archivo de audio en una variable IPlayable
        
        // TODO: Imprimir Via Interface: seguido del resultado de Play() desde la variable IPlayable
    }
}
quiz iconPonte a prueba

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