sábado, 15 de abril de 2017

Interfaces

Introducción


Un interfaz proporciona una lista de propiedades y métodos que posteriormente serán codificados en una o varias clases. Es decir, su naturaleza es declarativa, no contiene código de los miembros que expresa. En. NET, las interfaces se utilizan comúnmente para prestación de servicios para una clase. La interfaz  apoya algo que puede hacer el objeto de la clase, pero el servicio no está incluido en la relación es-un de la herencia.

Cuando utilizamos la herencia para hacer  nuevas clases diferentes de las clases existentes, si las clases están relacionadas por la herencia, se puede hacer referencia a una instancia derivada a través de una referencia a un valor de la clase base. Este comportamiento polimórfico no se limita a las clases derivadas. Visual Basic. NET ofrece otra construcción: la interfaz, que también se comporta de forma polimórfica

Interfaces con visual basic.NET



¿Por qué usar interfaces?

Las interfaces complementan a las clases. No hay porque pensar en que si se usan interfaces ya no se deben usar clases o viceversa.  Los interfaces nos permiten definir conjuntos reducidos de funcionalidades. El mismo interfaz implementado en distintas clases, puede tener un código distinto, por tanto con objetos de diferentes clases que implementen un mismo interfaz pueden tener comportamientos diferentes.

La principal utilidad de las interfaces se muestra en el siguiente ejemplo:

Necesitamos en nuestro programa modelar dos tipos de objetos de la vida real, unos serían los contratos de costo y otros serían contratos de venta. Para eso utilizo una clase para cada uno. Pero como los dos tipos de contrato tienen muchos elementos comunes, entonces hago una clase "padre" llamada Contrato donde defino esos métodos y propiedades comunes y después hago dos clases "hijas" que heredan de Contrato, que son "ContratoVenta" y "ContratoCosto". Hasta ahí todo bien, es un ejemplo muy sencillo de herencia.

Si en algún lugar de nuestro programa (un procedimiento) necesitamos realizar una operación con un contrato y esa operación la hacen tanto los contratos de venta, como de costo, entonces en lugar de escribir código para manejar un contrato de venta y código para manejar un contrato de costo, escribimos el código una sola vez y le pasamos una variable de tipo Contrato (la clase padre). De esa forma, el código que ejecuta la operación no sabe (ni le importa) si está tratando con un contrato de costo o uno de venta, al fin y al cabo la operación que se necesita hacer está definida en la clase padre y eso es todo lo necesario.

Ahora, hay tipos de objetos que no están tan relacionados entre sí como para que se justifique que hereden de una clase común. Sin embargo ambos tipos de objeto si tienen métodos o propiedades que son comunes o simplemente se llaman igual, aunque la implementación de
cada uno de ellos sea distinta.

Para poner un ejemplo fácil, tenemos una clase llamada ContratoVenta y otra clase llamada Factura. Esas dos clases tienen un método Imprimir que imprime su información y podíamos tener una rutina que reciba un objeto de cualquiera de estos dos tipos e imprima su información, invocando el método Imprimir. Esa rutina sabe que el objeto que le envían por parámetros tiene un método que se llama Imprimir y es el que realiza todo el trabajo. Sin embargo hay un problema: ¿de qué tipo es el objeto que le pasamos como parámetro a la rutina?

Pudiera ser:

Public Sub Imprimir (Objeto as Object)
Objeto.Imprimir
End Sub

Pero abusar del tipo Object es malo porque entre otras cosas consume muchos recursos de máquina. Otra idea sería hacer que las dos clases hereden de una común (como en el primer ejemplo), pero en este caso no es buena idea, ya que no hay relación entre ellas como para eso.

La solución es crear una Interfaz, que pudiera llamarse
IObjetoImprimible y cuenta con un método Imprimir. Esta interfaz la usan los objetos de tipo Contrato y los de tipo Factura y al final la rutina de impresión puede recibir un ObjetoImprimible:

Public Sub Imprimir (Objeto as ObjetoImprimible)
Objeto.Imprimir
End Sub

Si por ejemplo, tenemos una interfaz que se llama Ianimal. Esta publica dos métodos que son Caminar y Respirar. Derivamos una clase que se llama Perro, que hereda directamente de Ianimal y esta, también hereda los métodos. Estos no contendrán implementación, entonces puedes implementar (meter el código) para que el perro pueda caminar y respirar.
Por si misma, la interfaz no contiene código; contiene la estructura necesaria para crear clases de un tipo definido.
¿Es factible dejar de lado el concepto de clases y comenzar a utilizar las Interfaces en su lugar?
Pues no, no son equivalentes. Una  interfaz, es una declaración nada más. Es un 'contrato' que se firma entre el desarrollador que la escribe y el desarrollador que la usa, donde el primero te dice que si una clase implementa esa interface entonces contendrá esos métodos tal y como están definidos; el que usa la interface (para implementarla en una clase) se compromete a implementar todos y cada uno de los métodos declarados en la misma, tal cual como están definidos. Define el 'que' se puede hacer, dejando el 'como' hacerlo para cuando lo implementes en una clase. Y esa es la relación entre los dos conceptos: las interfaces se deben implementar en clases. Esto significa que en la clase escribes el código que implementa la funcionalidad de la interfaz. La interfaz es solamente la declaración, en la clase está el código real. En una clase puedes implementar más de una interfaz.

El Interfaz IMover.

Una interfaz es como una clase abstracta con todos los miembros abstractos. La interfaz sirve como un contrato que define qué métodos, propiedades y eventos de una clase debe implementar. En este documento, vamos a crear una interfaz, implementarlo en una clase, y utilizar la clase polimórficamente a través de una referencia de la interfaz.

La primera tarea es crear una interfaz
IMover implementarla en la clase CPeon de nuestro programa de ajedrez, este interfaz puede ser útil si se están moviendo objetos a su alrededor como parte del juego. Las clases en el proyecto pueden ser tan diferentes que no están relacionadas por la herencia, pero que tienen en común la capacidad de ser movidas. La interfaz IMover proporciona un interfaz estándar para el traslado de objetos. Esta interfaz se describe en el siguiente diagrama UML:
                                 
                             
Interfaces con visual basic

  
Este diagrama presenta un nuevo elemento UML: Una interfaz. Una propiedad se localiza en la sección de atributos en un elemento de clase, pero esta correspondencia desaparece en el caso de un elemento de la interfaz porque el elemento interfaz, tal como se define por el UML, no tiene una sección atributo. Como las interfaces no soportan implementación, no tienen datos de la instancia sino métodos solamente. Así pues, si necesitamos encajar el concepto de propiedad en la interfaz de UML, Los “captadores” get y “definidores” set se mostrarán como si fueran  métodos Get y Set. La interfaz de IMover contiene dos propiedades, X e Y, y un método mover que toma dos parámetros de dirección (arriba, abajo, izquierda, derecha) y la distancia.
El siguiente representa la interfaz en UML. Este estilo se utiliza con más frecuencia que la versión extendida mostrada anteriormente.


interfaces UML

                                         
En una empresa genérica sería muy interesante crear un interfaz IMantenimiento que contenga los métodos Alta, Baja y Modificación que sirvan para cualquier objeto del programa.
Así mismo son interesantes los interfaces para imprimir cualquier documento o para el acceso a la base de datos.


Definir el interfaz IMover.

Este ejemplo es una aplicación de consola en lugar de una aplicación de Windows. La salida del programa aparece en la ventana de comandos. La interfaz define la ubicación del objeto como propiedades X e Y e incluye un método Mover para mover el objeto alrededor.

1. Creamos un Nuevo proyecto de tipo  Console Application.

Nueva console application



2. En el menú de proyecto, elegimos Add New Item. Y elegimos Code File y lo llamamos IMover.

codefile



3. Añadimos el siguiente código al interfaz IMover interface.

Public Interface IMover
End Interface

4. Añadir una enumeración para indicar la dirección de movimiento del objeto. Añadir esta enumeración justo antes del interface.

Public Enum Direccion
    Arriba
    Abajo
    Derecha
    Izquierda
End Enum

5. Añadir dos declaraciones de propiedades en la definición del interfaz. La palabra clave public no está permitida en la definición de un interfaz. El propósito de un interfaz es definir  los métodos propiedades y eventos que deberá soportar una clase. Los miembros privados no tienen cabida en este contexto, la definición de Visual Basic permite los modificadores Readonly y Writeonly de las propiedades.

Public Interface IMover
    Property X() As Integer
    Property Y() As Integer
End Interface

6. Añadir la declaración Mover al interfaz.

Sub Mover(ByVal aDirection As Direccion, ByVal Distancia As Integer)

El interfaz está completo, para poder utilizarlo, es necesario implementar en interfaz en una clase.

Implementar el interfaz IMover en la clase CPeon.

Implementaremos las propiedades X e Y y el método Mover en la clase CPeon

Public Class CPeon
    Implements IMover
End Class
Visual Basic usa la palabra clave Implements  para indicar e el interfaz de una clase.

Notar que después de escribir Implements, Intellisense despliega una lista de interfaces con un icono similar al esquema de UML.

Interfaces predefinidos .NET



Al pulsar dentro de la clase, se escriben automáticamente los esquemas de las propiedades y el método que hay definidos en el interfaz IMover.

Public Sub Mover(ByVal aDirection As Direccion, ByVal Distancia As Integer) Implements IMover.Mover
End Sub

Public Property X() As Integer Implements Interfaz.IMover.X
Get
      End Get
      Set(ByVal value As Integer)
      End Set
End Property
Public Property Y() As Integer Implements Interfaz.IMover.Y
      Get
      End Get
      Set(ByVal value As Integer)
      End Set
End Property

Añadir un campo a la propiedad X e implementarla.

Public Property X() As Integer Implements Interfaz.IMover.X
Get
            Return m_x
      End Get
      Set(ByVal value As Integer)
            m_x = value
      End Set
End Property

No se puede utilizar Overrides cuando se están implementando miembros del interfaz. La interfaz es estrictamente un contrato sobre lo que se encuentra en la interfaz de clase.

Hay que tener en cuenta que se utiliza la palabra clave Implements de nuevo para especificar qué miembro de la interfaz está siendo implementado. La palabra clave Implements es seguida por el nombre completo del método. El nombre completo toma la forma Namespace.NombreClase.NombreMiembro. A menos que se haya añadido un  Namespace o cambiado las propiedades del proyecto por defecto, el Namespace es el mismo que el nombre del proyecto.

Agregar un campo para la propiedad Y, e implementar la propiedad Y.

Private m_y As Integer = 0
Public Property Y() As Integer Implements Interfaz.IMover.Y
Get
            Return m_y
      End Get
      Set(ByVal value As Integer)
            m_y = value
      End Set
End Property

Public Sub Mover(ByVal aDirection As Direccion, ByVal Distancia As Integer) Implements Interfaz.IMover.Mover
Select Case aDirection
            Case Direccion.Arriba
                m_y += Distancia
            Case (Direccion.Abajo)
                m_y -= Distancia
            Case (Direccion.Izquierda)
                m_x -= Distancia
            Case (Direccion.Derecha)
                m_x += Distancia
        End Select
End Sub

Añadir un método a la clase CPeon que no forma parte del interfaz IMover.

Private m_capturado As Boolean = False

Public Property Capturado() As Boolean
Get
            Return (m_capturado)
      End Get
      Set(ByVal Value As Boolean)
            m_capturado = Value
      End Set
End Property

Esto completa la implementación del interfaz IMover en la clase CPeon.

Testear el interface IMover.

Cuando se crean proyectos de tipo console application, Visual Studio .NET crea un método
start-up. Ahora añadiremos código a este método para testear la clase CPeon.

1. Hacer doble click sobre IMover.vb

2. Añadir código al método Main. Notar que movil es declarado como IMover e instanciado como CPeon, no se puede instanciar un interface, este no tiene implementación.

Sub Main()
Dim movil As IMover = New CPeon()
       
movil.X = 10
      movil.Y = 10
      Console.WriteLine("X:{0} Y:{1}", movil.X, movil.Y)
      Console.WriteLine("Moviendo arriba 5 espacios.")
      movil.Mover(Direccion.Arriba, 5)
      Console.WriteLine("X:{0} Y:{1}", movil.X, movil.Y)

      Dim unPeon As CPeon = CType(movil, CPeon)
      Console.WriteLine("Is the pawn captured? {0}", unPeon.Capturado)
End Sub

Utilizar una referencia a una interfaz es similar al uso de una referencia a una clase base. La  variable de referencia movil sólo tiene acceso a los miembros de IMover, aunque se puede establecer para referirse a una instancia de CPeon. Para acceder a los miembros de CPeon de la referencia de movil, se debe convertir la referencia a CPeon. Posteriormente podemos hacer que la referencia a movil apunte a otras piezas del juego, como la Torre o un Caballo. Cuando escribimos el código, buscamos en las listas de IntelliSense para ver estas diferencias.

3. Presionar Ctrl + F5 para ejecutar el programa. Si presionamos F5, la salida parpadea brevemente. Al ejecutar el programa con Ctrl + F5 nos da la oportunidad de examinar la salida.

interfaces




No hay comentarios:

Publicar un comentario