sábado, 11 de julio de 2009

C sharp NET/ Capítulo 9


C sharp NET/ Capítulo 9

Definición
Una Interfaz es una colección de miembros abstractos relacionados semánticamente. Una
interfaz representa un comportamiento que una clase dada puede soportar.
El número de miembros de una interfaz dependen del comportamiento que queramos
soportar, por ejemplo todos los objetos que sean móviles podrían querer soportar los
métodos acelerar y frenar.
Según la interfaz de C# una interfaz sería:
public interface IMovil
{
bool Acelerar(int n);
bool Frenar(int n);
}
También podríamos declarar dentro de la interfaz una propiedad que nos permita leer y/o
escribir la velocidad que queremos que tome nuestro objeto.
public interface IMovil
{
bool Velocidad{get; set;}
}
Dado que una interfaz es una colección de miembros abstractos cualquier clase o
estructura que quiera implementar una interfaz está obligada a implementar cada uno de
los métodos que se declaran en la interfaz. De esta forma se consigue un cierto tipo de
polimorfismo ya que si varias clases implementan la misma estructura tenemos la
posibilidad de tratar con todas ellas de la misma forma.
Seguramente alguien se preguntara por que usar interfaces pudiendo usar una clase base
abstracta definiendo los métodos anteriores como abstractos, la primera razón es
simplicidad, una clase base abstracta suele hacer más que definir una colección de
métodos, es capaz de definir métodos públicos, privados, protegidos y también metodos
concretos (estáticos) a los que pueden acceder todas las clases que deriven de ella mientras
que una interfaz se limita a definir una colección de métodos sin ninguna implementación.
La segunda razón es que C# solamente soporta herencia simple, pero sin embargo
podemos hacer que una clase implemente múltiples interfaces.
He aquí como haríamos para heredar de una clase base e implementar una interfaz,
teniendo en cuenta que VehiculoDeMotor sera nuestra clase base e IMovil nuestra interfaz.
public class CocheDeportivo : VehiculoDeMotor, IMovil
{
//Implementación de los métodos abstractos de vehículo
bool Acelerar(int n)
{
//implementación de Acelerar
}
C sharp NET/ Capítulo 9 2
bool Frenar(int n)
{
//implementación de Frenar
}
}
Hay que tener en cuenta que siempre hay que poner la clase base antes de las interfaces.
Ahora nuestra clase CocheDeportivo así como cualquier otra clase que implemente IMovil
podra acelerar y frenar, hay que tener en cuenta que si implementamos IMovil tendremos
que implementar absolutamente todos sus métodos.
Obteniendo Referencias a la Interfaz
Si hemos creado la clase CocheDeportivo podemos querer saber si éste soporta el
comportamiento de IMovil de modo que podemos hacer un cast explícito:
CocheDeportivo coche1 = new CocheDeportivo();
IMovil movil = (IMovil) coche1;
movil.Acelerar(30);
En caso de que nuestro objeto implemente la interfaz podríamos operar sobre él con todos
los métodos de la misma, pero en caso de que no la soporte tendríamos un error en tiempo
de ejecución, con lo cual la forma correcta de hacerlo es:
CocheDeportivo coche1 = new CocheDeportivo();
try{
IMovil movil = (IMovil) coche1;
movil.Acelerar(30);
}
catch(InvalidCastException e){
//gestión del error
}
Otra forma de hacerlo sin tener que recurrir a la gestión de excepciones sería utilizando la
palabra reservada as de C#:
CocheDeportivo coche1 = new CocheDeportivo();
IMovil movil;
movil = coche1 as IMovil;
if (movil != null)
movil.Frenar(10);
else
//otro tratamiento
La palabra reservada as pone la variable de tipo interfaz a null si la interfaz dada no está
soportada por el objeto.
Por último también podemos usar la palabra reservada is de C# para descubrir si un objeto
implementa o no una interfaz:
CocheDeportivo coche1 = new CocheDeportivo();
if (coche1 is IMovil)
C sharp NET/ Capítulo 9 3
coche1.Acelerar(10);
else
//otra gestión
Pasar interfaces como parámetros
Las interfaces son tipos de datos fuertemente tipados (valga la redundancia) de modo que
se pueden pasar como parámetros a métodos y se pueden usar también como valores de
retorno.
Hemos creado la interfaz IGiro de la siguiente manera:
public interface IGiro
{
void GirarDerecha(int grados);
void GirarIzquierda(int grados);
}
Y queremos que nuestro coche deportivo pueda girar a izquierda y derecha:
public class CocheDeportivo : VehiculoDeMotor, IMovil, IGiro
{
//Implementación de los métodos abstractos de vehículo
bool Acelerar(int n)
{
//Implementación de Acelerar
}
bool Frenar(int n)
{
//Implementación de frenar
}
void GirarDerecha(int grados)
{
//Implementación de GirarDerecha
}
void GirarIzquierda(int grados)
{
//Implementación de GirarIzquierda
}
}
Como hemos visto para soportar otra interfaz simplemente la añadimos al final después de
una ",".
Ahora supongamos que queremos hacer un método que nos provea de utilidades para el
giro por ejemplo hacer trompos, le podríamos pasar una interfaz IGiro de la siguiente forma
public class UtilsGiro
{
public static void Trompo(IGiro giro)
{
C sharp NET/ Capítulo 9 4
giro.GirarIzquierda(360);
}
}
Implementación Explícita de una Interfaz
Siguiendo con nuestro ejemplo de los coches definimos una nuevas interfaces:
public interface IAltaVelocidad
{
void Turbo(bool activar);
//resto de metodods
}
Como se puede ver nuestra interfaz implementa un método Turbo. ¿Qué pasaría si una
clase heredase a su vez de la clase Formula Uno que también implemente el metodo void
Turbo (bool)? Bueno vamos a verlo:
public class Campeon : Formula1, IAltaVelocidad
{
public override void Turbo(bool activar)
{
//gestion del turbo
}
}
Esto en un principio sería correcto pero qué pasa si hacemos lo siguiente:
Campeon miCampeon = new Campeon();
miCampeon.Turbo(true);
IAltaVelocidad iav = (IAltaVelocidad) miCampeon;
iav.Turbo(true);
Ambas veces se llamaría al mismo método, el definido en la clase Formula1, pero como
haríamos si quisiéramos tener dos Turbos diferentes? la respuesta es hacer que los métodos
definidos en la interfaz sean sólo accesibles desde una referencia a la interfaz, esto es lo
que se llama implementación explícita de una interface.
public class Campeon : Formula1, IAltaVelocidad
{
public override void Turbo(bool activar)
{
//gestión del turbo
}
void IAltaVelocidad.Turbo(bool activar)
{
//gestión del turbo
}
}
C sharp NET/ Capítulo 9 5
El segundo método sólo podrá ser llamado si usamos una referencia de tipo IAltaVelocidad
mientras que el primero podrá ser llamado usando una referencia a Campeon o a Formula1
(su clase base).
Existen algunas reglas extra al hacer esto, por ejemplo no podemos usar modificadores de
accesibilidad (public, private, protected) ya que si intentamos que sólo se pueda acceder al
método desde una referencia a la interfaz hacerlo sería contraproducente.
También hay que tener en cuenta que pueden haber colisiones de nombres entre clases
base e interfaces y entre interfaces entre si, técnicamente no existe ninguna diferencia y
todas pueden ser tratadas como hemos explicado arriba.
Jerarquías de interfaces
Las interfaces pueden servir de base para otras interfaces al igual que las clases, e igual
que en éstas la idea es que vayamos de lo general a lo particular.
Por ejemplo:
interface IVehiculo
{
void Acelerar();
void Frenar();
}
interface IVehiculoGasolina : IVehiculo
{
void CambiarVelocidadInyeccion(int velocidad);
}
interface IVehiculo4x4: IVehiculoGasolina
{
void Activar4x4(bool activar);
}
Al implementar una de estas interfaces en nuestra clase tenemos que implementar todos los
métodos de esta interfaz y de sus ancestros.
public class CocheDeJuguete : IVehiculo
{
void IVehiculo.Acelerar(int n)
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
}
public class CocheNormal:IVehiculoGasolina
{
void IVehiculo.Acelerar(int n)
C sharp NET/ Capítulo 9 6
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)
{
//Gestión de la inyeccion
}
}
public class TodoTerreno:IVehiculo4x4
{
void IVehiculo.Acelerar(int n)
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)
{
//Gestión de la inyeccion
}
void IVehiculo4x4.Activar4x4(bool activar)
{
//Gestión de 4x4
}
}
Y lógicamente las llamadas a los métodos serían:
TodoTerreno miTodoTerreno = new TodoTerreno();
((IVehiculo4x4)miTodoTerreno).Acelerar(20);
((IVehiculo4x4)miTodoTerreno).Frenar(20);
((IVehiculo4x4)miTodoTerreno).CambiarVelocidadInyeccion(1000);
((IVehiculo4x4)miTodoTerreno).Activar4x4(true);

No hay comentarios:

Publicar un comentario