DE .NET, SQLSERVER Y MÁS, APRENDE CONMIGO!✔

Desarrollo de todo tipo de aplicaciones y Administración de Base de datos con Tecnología Microsoft


UNETE

Interfaces

0

 


¿Que es la interfaz de una clase?

En teoría de orientación a objetos, la interfaz de una clase es todo lo que podemos hacer con ella. A efectos prácticos: todos los métodos, propiedades y variables públicas (aunque no deberían haber nunca variables públicas, debemos usar propiedades en su lugar) de la clase conforman su interfaz.

Dada la siguiente clase:

class Contenedor
{
  
public int Quitar();
  
public void Meter(int v);
  
private bool EstaRepetido(int v);
}

Su interfaz está formada por los métodos Quitar y Meter. El método EstaRepetido no forma parte de la interfaz de dicha clase, ya que es privado.

En orientación a objetos decimos que la interfaz de una clase define el comportamiento de dicha clase, ya que define que podemos y que no podemos hacer con objetos de dicha clase: dado un objeto de la clase Contenedor yo puedo llamar al método Quitar y al métdo Meter pero no puedo llamar al método EstaRepetido.

Así pues: toda clase tiene una interfaz que define que podemos hacer con los objetos de dicha clase.

2. Interfaces idénticas no significa clases intercambiables

Fíjate en estas dos clases:

class Contenedor
{
  
public int Quitar() { ... }
  
public void Meter (int v) { ... }
}
class OtroContenedor
{
  
public int Quitar() { ... }
  
public void Meter (int v) { ... }
}

Que puedes deducir de ellas? Exacto! Su inerfaz es la misma: con ambas clases podemos hacer lo mismo: llamar al método Quitar y al método Meter.

Ahora imagina que en cualquier otro sitio tienes un método definido tal y como sigue:

public void foo (Contenedor c)
{
  
// Hacer cosas con c como p.ej:
  
int i = c.Quitar();
   c.Meter(10);
}

El método recibe un Contenedor y opera con él. Ahora dado que las interfaces de Contenedor y OtroContenedor son iguales, uno podría esperar que lo siguiente funcionase:

OtroContenedor oc = new OtroContenedor();
foo(oc);

Pero esto no va a compilar. ¿Por que? Pues aunque nosotros somos capaces leyendo el código de comparar la interfaz de ambas clases, el compilador no puede hacer esto. Para el compilador Contenedor y OtroContenedor son dos clases totalmente distintas sin ninguna relación. Por lo tanto un método que espera un Contenedor no puede aceptar un objeto de la clase OtroContenedor.

Quiero recalcar que el hecho de que el compilador no compare las interfaces de las clases no se debe a una imposibilidad técnica ni nada parecido: se debe a que no tiene sentido hacerlo.

¿Por que? Pues simplemente porque las interfaces son idénticas por pura casualidad. Supón que fuese legal llamar a foo con un objeto OtroContenedor, ok?

Entonces podría pasar lo siguiente:

1.      Alguien añade un método público a la clase Contenedor.

2.      Se modifica el método foo para que llame a dicho método nuevo. Eso es legal porque foo espera un Contenedor como parámetro

3.      La llamada a foo(oc) donde oc es OtroContenedor… como debe comportarse ahora? OtroContenedor no tiene el método nuevo que se añadió a Contenedor!

Así pues: dos clases con la misma interfaz no tienen relación alguna entre ellas y por lo tanto no se pueden intercambiar.

3. Implementación de interfaces

El ejemplo anterior ejemplifica un caso muy común: tener dos clases que hacen lo mismo pero de diferente manera. P.ej. imagina que Contenedor está implementado usando un array en memoria y OtroContenedor está implementando usando, que sé yo, pongamos un fichero en disco. La funcionalidad (la interfaz) es la misma, lo que varía es la implementación. Es por ello que en programación orientada a objetos decimos que las interfaces son funcionalidades (o comportamientos) y las clases representen implementaciones.

Ahora bien, si dos clases representan dos implementaciones distintas de la misma funcionalidad, es muy enojante (y estúpido) que no las podamos intercambiar. Para que dicho intercambio sea posible C# (y en general cualquier lenguaje orientado a objetos) permite explicitar la interfaz, es decir separar la declaración de la interfaz de su implementación (de su clase). Para ello usamos la palabra clave interface:

interface IContenedor
{
  
int Quitar();
  
void Meter(int i);
}

Este código declara una interfaz IContenedor que declara los métodos Quitar y Meter. Fíjate que los métodos no se declaran como public (en una interfaz la visibilidad no tiene sentido, ya que todo es public) y que no se implementan los métodos.

Las interfaces son un concepto más teórico que real. No se pueden crear interfaces. El siguiente código NO compila:

IContenedor c = new IContenedor();
// Error: No se puede crear una interfaz!

Es lógico que NO podamos crear interfaces, ya que si se nos dejara, y luego hacemos c.Quitar()… que método se llamaría si el método Quitar() no está implementado?

Aquí es donde volvemos a las clases: podemos indicar explícitamente que una clase implementa una interfaz, es decir proporciona implementación (código) a todos y cada uno de los métodos (y propiedades) declarados en la interfaz:

class Contenedor : IContenedor
{
  
public int Quitar() { ... }
  
public void Meter(int i) { ... }
}

La clase Contenedor declara explícitamente que implementa la interfaz IContenedor. Así pues la clase debe proporcionar implementación para todos los métodos de la interfaz. El siguiente código p.ej. no compila:

class Contenedor : IContenedor
{
  
public void Meter(int i) { ... }
}
// Error: Y el método Quitar()???

Es por esto que en orientación a objetos decimos que las interfaces son contratos, porque si yo creo la clase la interfaz me obliga a implementar ciertos métodos y si yo uso la clase, la interfaz me dice que métodos puedo llamar.

Y ahora viene lo bueno: Si dos clases implementan la misma interfaz son intercambiables. Dicho de otro modo, en cualquier sitio donde se espere una instancia de la interfaz puede pasarse una instancia de cualquier clase que implemente dicha interfaz.

Podríamos declarar nuestro método foo anterior como sigue:

void foo(IContenedor c)
{
  
// Cosas con c...
   c.Quitar();
   c.Meter(10);
}

Fíjate que la clave es que el parámetro de foo está declarado como IContenedor, no como Contenedor o OtroContenedor, con esto indicamos que el método foo() trabaja con cualquier objeto de cualquier clase que implemente IContenedor.

Y ahora, si supones que tanto Contenedor como OtroContenedor implementan la interfaz IContenedor el siguiente código es válido:

Contenedor c = new Contenedor();
foo(c);   
// Ok. foo espera IContenedor y Contenedor implementa IContenedor
OtroContenedor oc =
new OtroContenedor();
foo(oc);
// Ok. foo espera IContenedor y OtroContenedor implementa IContenedor
// Incluso esto es válido:
IContenedor ic =
new Contenedor();
IContenedor ic2 =
new OtroContenedor();

¿Cuando usar interfaces?

En general siempre que tengas, o preveas que puedes tener más de una clase para hacer lo mismo: usa interfaces. Es mejor pecar de exceso que de defecto en este caso. No te preocupes por penalizaciones de rendimiento en tu aplicación porque no las hay.´

No digo que toda clase deba implementar una interfaz obligatoriamente, muchas clases internas no lo implementarán, pero en el caso de las clases públicas (visibles desde el exterior) deberías pensarlo bien. Además pensar en la interfaz antes que en la clase en sí, es pensar en lo que debe hacerse en lugar de pensar en como debe hacerse. Usar interfaces permite a posteriori cambiar una clase por otra que implemente la misma interfaz y poder integrar la nueva clase de forma mucho más fácil (sólo debemos modificar donde instanciamos los objetos pero el resto de código queda igual).

DEMOSTRACIÓN

En Visual Studio vamos a dar click en menú File à New à Project


Seleccionamos la opción ‘Console Application’ y colocamos como nombre ‘DemoAppInterfaces’ y damo click en el botón OK


Fíjese que se agrega nuestro proyecto al navegador de soluciones

Fíjese nuevamente que en el proyecto ‘DemoAppInterfaces’ existe un archivo de nombre ‘Program.cs’, le damos doble click en él, y procedemos a escribir nuestra interface que sera IAve la cual contendra un par de metodos que todas las aves realizan Volar() y Comer().


Ahora crearemos una clase la cual implementara la interface anteriormente realizada, como se muestra a continuación:





Como vemos hemos implementado ambos metodos de nuestra interface(Volar();, Comer();) en caso de no implementarlos se generaria un error debido a que estas propiedades o metodos deben de ser obligatorios para poder implementar la interface en nuestra clase.

Ahora crearemos otra clase en la cual implementaremos nuestra clase AvePropiedades la cual contiene un constructor el cual nos inicializa la variable Nombre.


Como podemos notar nuestro contructor de la clase Ave nos pide como parametro un string el cual inicalizara nustra clase padre o base AvePropiedades.

Enseguida implementaremos nuestra clase Ave en un código simple.


Ejecutamos


El resultado es el siguiente:






Tal vez te interesen estas entradas

No hay comentarios