CodeGym /Cursos /C# SELF /Concepto de interfaz y su sintaxis

Concepto de interfaz y su sintaxis

C# SELF
Nivel 23 , Lección 0
Disponible

1. ¿Qué es una interfaz?

Si una clase abstracta es como un "semielaborado" con implementación parcial, entonces una interfaz es simplemente una lista de requisitos: lo que debe saber hacer un ser (clase) para que se pueda usar en cierto contexto abstracto.

En programación, una interfaz es como un "contrato" o "lista de requisitos" para el comportamiento de un objeto. Describe un conjunto de métodos públicos, propiedades, indexadores y eventos que la clase que implementa esa interfaz debe proporcionar. La interfaz dice: "Si quieres llamarte, por ejemplo, IDriveable (capaz de conducir), entonces tienes que tener los métodos Drive() y Stop()".

Puedes imaginar una interfaz como una Lista de Requisitos para un Empleado: por ejemplo, si quieres contratar "Cocineros", en la interfaz pondrás: "Debe saber cocinar, servir platos, lavarse las manos". Cómo resuelve esas tareas cada cocinero — es cosa suya. Lo importante es que desde fuera cumpla el contrato.

Importante: la interfaz describe solo qué debe proporcionar la clase, no cómo lo hace.

En resumen, en términos de OOP

  • La interfaz describe la "apariencia" externa (API) del objeto: qué acciones permite realizar.
  • No contiene estado (ningún campo). En el sentido clásico, las interfaces tampoco contienen implementación, pero en versiones modernas de C# hay excepciones (por ejemplo, métodos por defecto), de las que hablaremos más adelante.
  • Una clase puede implementar tantas interfaces como quiera (¡a diferencia de la herencia de clases!).

Analogía de la vida real

Puerto USB — todo el mundo sabe que puedes conectar un ratón, teclado, pendrive, auriculares, ¡incluso una cafetera (sí, existen!)! Lo que hay dentro del "ratón" no importa, mientras tenga conector USB y el dispositivo soporte el protocolo correcto. ¡Eso es una interfaz!

¿Para qué sirven las interfaces?

  • Reducen el acoplamiento. El código trabaja con la interfaz, no con una clase concreta. Programamos "al nivel de la interfaz".
  • Permiten implementar herencia múltiple de comportamiento: una clase puede implementar varias interfaces.
  • Estandarización: puedes crear mecanismos universales: por ejemplo, todos los objetos que se pueden comparar implementan la interfaz IComparable.
  • Testabilidad: es fácil sustituir una implementación concreta por un "stub" en los tests.
  • Pluginabilidad: puedes añadir nuevos "módulos" sin cambiar el código existente.

2. Sintaxis de declaración de una interfaz

¡Vamos al código! En C#, una interfaz se declara con la palabra clave interface, y los nombres de las interfaces suelen empezar por I :


// Declaración de la interfaz
public interface IPrintable
{
    // Método abstracto — del contrato
    void Print();

    // También se pueden declarar propiedades
    string Name { get; set; }
}

Recuerda: los métodos y propiedades de la interfaz NO tienen implementación (no hay cuerpo del método, solo la firma — igual que en los métodos abstractos).

Características clave de la interfaz

  • No se pueden declarar campos (variables miembro) dentro de una interfaz.
  • Todos los métodos, propiedades, eventos e indexadores por defecto son public (y así deben ser en la implementación).
  • La interfaz no puede contener constructores (ya que no tiene estado).
  • La interfaz no depende de si la clase que la implementa es abstracta o concreta.

Interfaz en acción

Vamos a integrar una interfaz en nuestra app de aprendizaje. Ahora tenemos la interfaz "imprimible" (IPrintable), que implementa la clase Report y, por ejemplo, una nueva clase Invoice.

public interface IPrintable
{
    void Print();
    string Name { get; set; }
}

Ahora definimos una clase que implementa esta interfaz:

public class Report : IPrintable
{
    public string Name { get; set; }

    public Report(string name)
    {
        Name = name;
    }

    // Implementación del método de la interfaz
    public void Print()
    {
        Console.WriteLine($"Imprimiendo informe: {Name}");
    }
}

Y ahora — una clase totalmente diferente, pero con la misma interfaz:

public class Invoice : IPrintable
{
    public string Name { get; set; }

    public Invoice(string name)
    {
        Name = name;
    }

    public void Print()
    {
        Console.WriteLine($"Imprimiendo factura: {Name}");
    }
}

Ahora podemos escribir un método que trabaje con cualquier entidad imprimible:

public static void PrintAnything(IPrintable printable)
{
    printable.Print(); // ¡Eso es! Da igual si es informe o factura — lo importante es que sepa imprimir.
}

Y aquí tienes un ejemplo de uso:

var report = new Report("Informe mensual");
var invoice = new Invoice("Factura #12345");

PrintAnything(report);  // Imprimiendo informe: Informe mensual
PrintAnything(invoice); // Imprimiendo factura: Factura #12345

Así es como las interfaces te permiten escribir código universal, extensible y bonito.

3. Implementación de interfaces

Conectar una interfaz a una clase

Una interfaz se implementa en una clase usando dos puntos (sí, igual que en la herencia):

public class Ticket : IPrintable
{
    public string Name { get; set; }
    public void Print()
    {
        Console.WriteLine($"Imprimiendo billete: {Name}");
    }
}

Importante: la clase debe implementar todos los miembros de la interfaz. Además, los miembros implementados deben ser public.

Si te olvidas de implementar aunque sea un miembro:

public class BrokenTicket : IPrintable
{
    // Falta la implementación de Print()
    public string Name { get; set; }
}
// Error de compilación: 'BrokenTicket' no implementa el miembro de la interfaz 'IPrintable.Print()'

Varias interfaces

Una clase puede implementar varias interfaces separadas por coma:

public interface IStorable
{
    void Store();
}

public class MultiPurposeDoc : IPrintable, IStorable
{
    public string Name { get; set; }
    public void Print()
    {
        Console.WriteLine("Imprimiendo documento");
    }

    public void Store()
    {
        Console.WriteLine("Guardando documento");
    }
}

4. ¿Para qué sirven las interfaces?

Quizá ahora mismo estés pensando: "Vale, la sintaxis está clara. ¿Pero para qué sirve todo esto en la vida real, aparte de complicarme la vida en este curso?" ¡Déjame convencerte! Las interfaces son una de las herramientas más usadas en desarrollo profesional.

Separación de responsabilidades y bajo acoplamiento (Decoupling / Loose Coupling):

  • Imagina que desarrollas un reproductor de música. Le da igual de dónde viene la música — de un archivo local, de internet o de un CD. Solo le importa que la fuente de música pueda proporcionar un flujo de audio.
  • Puedes definir la interfaz IAudioSource con el método GetAudioStream().
  • Entonces tendrás clases como FileAudioSource, InternetAudioSource, CDAudioSource, que implementan esa interfaz.
  • Tu reproductor trabajará con IAudioSource, sin saber el tipo concreto. Si mañana aparece un nuevo tipo de fuente, por ejemplo, BluetoothAudioSource, ¡no tendrás que cambiar el código del reproductor! Solo crea una nueva clase que implemente IAudioSource. Así tu sistema es mucho más flexible y fácil de ampliar. Esto es bajo acoplamiento — los componentes dependen de abstracciones (interfaces), no de implementaciones concretas.

Polimorfismo y tratamiento uniforme:

Como vimos en el ejemplo de PrintAnything, puedes tener un conjunto de objetos de distintos tipos, pero que comparten un comportamiento común descrito en la interfaz. Puedes llamar al mismo método (Print()) en todos esos objetos, sin saber quién es exactamente — informe, factura o billete. Esto te permite escribir código muy conciso y universal.

Testing (Unit Testing):

Esto es, probablemente, uno de los usos más importantes de las interfaces. Cuando pruebas un componente de tu sistema, a menudo necesita otros componentes para funcionar (por ejemplo, una clase que guarda datos puede depender de una clase que trabaja con la base de datos).

En vez de pasar la clase real DatabaseSaver (¡que requiere una base de datos real para testear!), puedes pasar un objeto "falso" (o "mockeado") que simplemente implementa la interfaz IDataSaver. Ese objeto "mockeado" solo simulará el guardado, sin tocar la base real. Así puedes testear componentes de forma aislada, rápido y sin dependencias externas.

Desarrollo de APIs y frameworks:

Cuando creas una librería o framework, quieres dar a los desarrolladores "puntos de extensión". Las interfaces son perfectas para esto. Puedes decir: "Si quieres que tu componente funcione con mi sistema, implementa esta interfaz". Las librerías estándar de .NET están llenas de interfaces (por ejemplo, IEnumerable<T>, IDisposable, IComparable<T>) — definen contratos para los escenarios más comunes.

Programar directamente al nivel de la interfaz (Programming to an Interface):

Los desarrolladores con experiencia suelen decir: "Programa al nivel de la interfaz, no de la implementación". Eso significa que cuando defines el tipo de una variable o parámetro de un método, en vez de usar una clase concreta (Car) es mejor usar la interfaz (IDriveable). Así tu código es más flexible y menos dependiente de los detalles de implementación, permitiendo cambiar una implementación por otra fácilmente.

5. Errores típicos al trabajar con interfaces

Error nº1: intentar crear una instancia de una interfaz.
Puedes escribir Cat catName = new Cat("Murzik", 3);, porque Cat es una clase concreta. Pero no puedes escribir ITalkable talker = new ITalkable();. Una interfaz es solo un contrato, un esquema. No tiene implementación y no se puede crear directamente. Es como un plano, no una casa terminada.

Error nº2: olvidar implementar todos los miembros de la interfaz.
Si indicas que tu clase implementa una interfaz, por ejemplo IMyInterface, entonces debe implementar todos sus métodos. Incluso si te olvidas de uno, tendrás un error de compilación: MyClass no implementa IMyInterface.TheMissingMethod().

Error nº3: modificadores de acceso incorrectos en la implementación.
Los métodos de la interfaz son implícitamente public, y en la implementación también deben ser public. Si intentas hacer el método private o protected, el compilador dará error. Si lo prometiste — cúmplelo abiertamente.

Error nº4: intentar añadir campos o constructores en la interfaz.
Las interfaces describen comportamiento, no estado. Por eso no puedes añadir campos ni constructores. Si lo intentas — tendrás un error de compilación. Solo se permiten propiedades, y solo como descripción de getters/setters.

Error nº5: confundir override con la implementación de la interfaz.
La palabra clave override se usa para sobrescribir métodos de la clase base. Pero al implementar una interfaz no hace falta — solo escribes el método public con la firma correcta. Es un matiz importante que se olvida fácil.

2
Tarea
C# SELF, nivel 23, lección 0
Bloqueada
Declaración de una interfaz simple y su implementación
Declaración de una interfaz simple y su implementación
2
Tarea
C# SELF, nivel 23, lección 0
Bloqueada
Varios clases con una interfaz común
Varios clases con una interfaz común
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION