miércoles, 11 de septiembre de 2013

Usar la herencia para crear clases especializadas

Ajedrez, un programa de ejemplo


Para explicar la herencia, vamos a crear un nuevo programa que juegue al ajedrez, para ello crearemos una clase base llamada CPieza con los atributos comunes a todas las piezas y de ella derivarán las clases hijas CTorre, CCaballo, CAlfil, CDama, CRey y CPeon que incluirán la sobrecarga del método mover() específica de cada tipo de pieza.


Cada pieza incluye los siguientes atributos comunes: Nombre, Color, un atributo booleano que indique si la pieza está en Juego y por último otro atributo que contenga su posición en el tablero.

Usar la herencia para crear clases especializadas
      Private m_nombre as string
      Private m_color as boolean
      Private m_enjuego as boolean
      Private m_posicion as string

Utilizando la herencia, se puede crear una nueva clase de añadiendo o modificando una clase existente. En este documento, vamos a crear una primera clase, CPieza, y a continuación, utilizaremos  la herencia para crear clases especializadas como CTorre, CCaballo, CAlfil etc. La herencia no se limita a las clases que se creen; se puede heredar de muchas de las clases de Microsoft. NET.

El código de cliente creará un array de objetos, con la generación de todas las piezas del tablero, asignando a cada una de ellas sus atributos correspondientes, luego al jugar, se llamará a los diferentes métodos mover, que irán modificando los atributos de cada pieza en función del transcurso de la partida.


Usar la herencia para crear clases especializadas

Esquema teórico


Para evitar errores y escribir programas potentes, se recomienda utilizar la herencia y tener en cuenta las siguientes consideraciones de diseño.


Herencia


La relación tiene-un (clase),  es-un (objeto) es fundamental en el diseño orientado a objetos. Se crean clases que contienen instancias de otras clases. Estos diseños de modelo tienen una relación entre un objeto y sus propiedades (un formulario tiene-un botón)  permiten crear una aplicación mediante la combinación de los objetos ya existentes.

La herencia es el método de programación utilizado para implementar la relación es-un  de  los objetos. Una caja de texto es un control, un coche es un vehículo, un alfil es una pieza de ajedrez. Si ya se ha escrito el código para el modelo propietario de una pieza de ajedrez, CPieza, nos gustaría poder ser capaces de utilizar ese código para todas las piezas del tablero.

La herencia consiste en la creación de nuevas clases derivadas o hijas a partir de clases existentes. A la clase padre también se le puede llamar clase base. También se puede hablar de superclase, clase y subclase, pero la terminología clase base y clase derivada es más adecuada para la programación con Visual Basic.

La relación es-un


Tener en cuenta que la clase derivada que creemos necesite realmente derivarse de la clase base elegida. Es decir que el objeto creado sea realmente un objeto especial derivado del objeto base. Una forma de saberlo es hacerse la pregunta es-un.  ¿Una torre es-una pieza de ajedrez? Si tratamos de eliminar propiedades o métodos de una clase base, debe advertirnos de si realmente el objeto creado debe derivar de la clase base elegida. Por ejemplo si estamos tratando de eliminar el evento mover de la clase pieza para alojar el tablero, es evidente que lo estamos haciendo mal. El tablero NO es-una pieza.

Polimorfismo


Una pieza de ajedrez es un único tipo de pieza de ajedrez CPieza. Pero hay piezas que son peones, torres, alfiles, etc aunque derivan todas de pieza de ajedrez. A esto se llama comportamiento polimórfico y permite a los desarrolladores utilizar una variable Pieza para referirse a cualquiera de las clases derivadas de una pieza de ajedrez. El Polimorfismo describe el comportamiento de las clases que se derivan de una clase base común, También permite que cada clase derivada maneje un método con idéntico nombre pero con un comportamiento diferente. Por ejemplo, el método mover estará presente en cada clase derivada, CPeon, CReina, CCaballo, etc pero será diferente para cada una de ellas.
Aquí nos centraremos en los mecanismos de la herencia. Se pueden desarrollar resultados sofisticados heredando de clases de. NET. Incluso con implementaciones sencillas.

Debemos hacer un estudio de nuestras clases para ver si realmente necesitamos utilizar la herencia. O al contrario, si tenemos muchas clases inconexas debemos preguntarnos si algunas se pueden derivar de otras. También es posible que la clase sólo necesite contener una instancia de la clase en lugar de servir como clase base.

Comprobación de tipos


En general, controlar el  tipo (utilizando el operador type) es un indicio de que se está utilizando la herencia de forma incorrecta. Determinar si se están definiendo correctamente las propiedades y métodos para que la comprobación de tipos no sea necesaria.


Seleccionar o cambiar los estados


Si el código contiene muchos selects o switchs, hay que considerar si el uso de la herencia podría  simplificar el código.

  CPieza: Un ejemplo sencillo


En este ejemplo crearemos una clase base llamada CPieza con los atributos genéricos de una pieza de ajedrez, como su color o su posición, y luego se derivarán varias clases CCaballo, CTorre etc, (Recordar que son derivadas por que un caballo es una pieza de ajedrez, una torre es una pieza de ajedrez, etc.) que además incluirán el comportamiento específico de cada tipo de pieza. En estas clases irá el método Mover() que será específico para cada clase de pieza.

Descripción de los Miembros


programar un juego de ajedrez

 m_nombre as string
Propiedad de sólo lectura que identifica la pieza por su nombre. TD  torre de la dama PTD Peón de torre de la dama, etc.

m_color as boolean
Propiedad de sólo lectura que identifica el color de la pieza  (True = blanca, False = Negra).

Posicion as string
Propiedad que identifica mediante unas coordenadas la posición de cada pieza sobre el tablero.  (Ver esquema de la izquierda)

EnJuego as bolean
Propiedad que identifica si la pieza está en juego (True) o ha sido eliminada (False).


Public Function Mover(Byval Posicion as string) as boolean
Método que será definido en las clases hijas para definir como se mueve cada pieza, pasando como parámetro la casilla de destino este devolverá true si la posición es correcta y false si es prohibida.

Crear la clase base CPieza


Public Class CPieza

    Private m_nombre As String
    Private m_color As Boolean
    Private m_posicion As String
    Private m_enjuego As Boolean

    Public ReadOnly Property Nombre() As String
        Get
            Return m_nombre
        End Get
    End Property

    Public ReadOnly Property Color() As String
        Get
            Return m_color
        End Get
    End Property

    Public Property Posicion() As String
        Get
            Return m_posicion
        End Get
        Set(ByVal value As String)
            m_posicion = value
        End Set
   End Property

    'True si la pieza está en juego, False si la pieza ha sido      capturada
    Public Property EnJuego() As Boolean
        Get
            Return m_enjuego
        End Get
        Set(ByVal value As Boolean)
            m_enjuego = value
        End Set
    End Property

    ‘Constructor
    Public Sub New()
        m_nombre = "TD" 'torre Dama
        m_color = True
        m_posicion = "A1"
        m_enjuego = True
    End Sub
End Class

Creando la clase derivada CTorre


La primera clase derivada se crea será la clase CTorre. En este ejemplo CTorre es idéntica a la clase CPieza, excepto en el método Mover que es específico para la torre.

Public Class CTorre
    Inherits CPieza

End Class

Examinar el entorno de desarrollo


Si pulsamos el botón para ver el diagrama de clases.

Ver el diagrama de clases

Podemos ver un esquema de las clases creadas

diagrama de clases ajedrez

También podemos consultar el visor  de clases

clases del ajedrez

Crear campos de clase usando la palabra clave Protected


Saltar el seguro de protección private.

Para que la clase hija pueda acceder a los campos de la clase padre pero estos sigan siendo privados para el código cliente, se usa la palabra clave protected.

Public Class CPieza
    Protected m_color As Boolean

Ahora en el Class View aparece con una llave. 

clases y propiedades del ajedrez

Crear métodos y propiedades sobrecargados, Overridable


Sobreescritura de miembros, para ello se  hace esto en la clase base.

'Método padre vacio, se definirá en cada clase hija
Public Overridable Function Mover(ByVal NuevaPos As String) As Boolean
        Me.Posicion = NuevaPos
        Return True
End Function
Esta propiedad o método podrá ser sobreescrito en las clases derivadas.

Crear métodos y propiedades sobrecargados Overrides


Para indicarlo en la clase derivada lo marco con la palabra clave Overrides.

'Dada una torre se le indica moverse desde su posición actual a otra posición.
'True si la nueva posisión es correcta, False si es prohibida
Public Overrides Function Mover(ByVal NuevaPos As String) As Boolean

      Dim PosActual As String
      Dim ColActual As String
      Dim FilActual As String
      Dim NuevaCol As String
      Dim NuevaFil As String

        PosActual = Me.Posicion

        ColActual = Mid(PosActual, 1, 1)
        FilActual = Mid(PosActual, 2, 1)

        NuevaCol = Mid(NuevaPos, 1, 1)
        NuevaFil = Mid(NuevaPos, 2, 1)

        If ColActual = NuevaCol Then
            'si se mueve por la misma columna, no importa el valor de la fila, es movimiento correcto
            Me.Posicion = NuevaPos
            Return True
        Else
            If FilActual = NuevaFil Then
                'Si se mueve por la misma fila, no importa el valor de la columna, es movimiento correcto
                Me.Posicion = NuevaPos
                Return True
            Else
                'En cualquier otro caso es movimiento prohibido
                Me.Posicion = PosActual
                Return False
            End If
        End If
       'En cualquier otro caso
        Me.Posicion = PosActual
        Return false
End Function

También se permite el uso de la palabra NotOverridable para marcar explícitamente un miembro como no reemplazable. (Este es el comportamiento predeterminado)

- No podrá cambiar el atributo ReadOnly ni WriteOnly si se quiere sobrescribir una propiedad en una clase derivada.

- No se pueden sobrescribir campos, constantes ni miembros compartidos definidos en la clase base.

- De forma predeterminada los métodos que estén marcados como Overrides en las clases derivadas son reemplazables (Overridable) en las siguientes clases derivadas, por lo que nunca utilizará simultáneamente las palabras Overrides y Overridable.

- Solo se podrá utilizar la palabra NotOverridable en conjunto con la palabra Overrides para declarar un método como No reemplazable.

- No necesitará introducir la palabra Overrides en la clase derivada ni la palabra Overridable en la clase base si está agregando un miembro con el mismo nombre en la clase derivada (Aunque note un mensaje de advertencia).

Además de que no es obligatoria la palabra  Overloads en el miembro de la clase derivada.
A esto se le conoce con el nombre de ‘Sombreado de Miembros’ (Shadows) y podemos indicarle al compilador que interprete estas advertencias como errores o que simplemente las ignore. (Ver propiedades del proyecto).

Usar la palabra clave MyBase


Esta palabra resulta útil cuando desea hacer referencia a un campo, propiedad o método de la
Clase base. Si no ha reemplazado a algún miembro en la clase derivada la expresión Me.miembro y MyBase.miembro harán referencia al mismo miembro y ejecutarán el mismo código, sin embargo cuando en la clase derivada se ha redefinido el Miembro, se tendrá que usar la palabra Mybase si se quiere hacer referencia al miembro de la clase base.

Si en la clase derivada necesito llamar al método sobrecargado de la clase padre utilizo MyBase para evitar sobrecargar la pila.

Public Overrides Property Color() As String

        mybase.Color()
     
End Property

Usar la palabra clave Me


Si es necesario referirse a un propiedad, campo o función de la propia clase o de una clase padre, se puede poner me para referirnos a un objeto de esta clase.

Public Class CPieza

Private m_posicion As String

Public Property Posicion() As String
      Get
            Return m_posicion
      End Get
      Set(ByVal value As String)
            m_posicion = value
      End Set
End Property

Public Function Mover(ByVal NuevaPos As String) As Boolean

        Dim PosActual As String
        PosActual = Me.Posicion
  
End Function

End Class

Añadir el constructor


Los constructores no se heredan, por lo que se debe agregar a la clase derivada. Además, cada vez que se define un constructor hay que incluir una llamada implícita al constructor sin parámetros que pertenece a la clase base.

Public Class CPieza
    Public Sub New()
        Dim posicion As New CTablero
        m_color = True
        posicion.Columnas(1) = 1
        posicion.Filas(1) = 1
        m_posicion = posicion
        m_enjuego = True
    End Sub
End Class

Public Class CTorre
    Inherits CPieza

    Private Sub New()
        MyBase.New()
    End Sub
End Class

Public Class CCaballo
    Inherits CPieza

    Private Sub New()
        MyBase.New()
    End Sub
End Class

Herencia en un control: La Clase CBotonEliptico


Gracias a la herencia, es posible derivar clases de nuestras propias clases pero también de las  clases base suministradas por .NET.  A Continuación crearemos una clase llamada CBotonEliptico que derive de la clase base System.Windows.Forms.Button en este caso sólo será necesario reemplazar el método OnPaint de la clase Button.

Creamos la clase CBotonEliptico, para crear una clase derivada de una clase. NET, se declara la clase e indicamos la clase base.

Añadir la clase  CBotonEliptico


Public Class CBotonEliptico
    Inherits Button
End Class

Sobreescribiendo el Método OnPaint


El método OnPaint se llama cada vez que se dibuja el control en el formulario. La clase base dibuja el rectángulo familiar. Reemplazando el método OnPaint, se puede determinar el aspecto del botón.

Protected Overrides Sub OnPaint(ByVal pevent As System.Windows.Forms.PaintEventArgs)
      Me.Size = New Size(50, 70)
      Dim bEliptico As System.Drawing.Drawing2D.GraphicsPath = New           System.Drawing.Drawing2D.GraphicsPath()
      bEliptico.AddEllipse(New System.Drawing.RectangleF(0, 0, 50,70))
      Me.Region = New Region(bEliptico)
End Sub

Agregar el siguiente código para dibujar el botón elíptico. Además de elíptico, el área clickable del botón también deber ser elíptica.

Para hacer un control que asuma una forma particular, en este caso elíptico, debe
definir su propiedad Región para que se logre esa forma. Se puede crear la forma, utilizando el objeto
GraphicsPath. El objeto GraphicsPath permite crear un dibujo. En este ejemplo, se crea un dibujo mediante la adición de un círculo de GraphicsPath. El tamaño del botón está limitado a 50 por 70 píxeles de modo que se ve una elipse y rellena.

Uso de la Clase


La clase CBotonEliptico se ha definido en el archivo de origen y no aparece en la
caja de herramientas para el arrastre en el formulario. Para agregar una instancia
CBotonEliptico al Form1, se pueden  utilizar los mismos métodos que se utilizaron para crear botones en el anterior documento.

Añadir el Botón Elíptico al formulario


Private Sub BotonEliptico_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        MessageBox.Show("Hola")
End Sub

En el Form_Load del formulario creamos el objeto

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
      Dim rb As New CBotonEliptico()
      Me.Controls.Add(rb)
      AddHandler (rb.Click), AddressOf BotonEliptico_Click

End Sub

Ejecutando la aplicación tenemos

botón elíptico


Y si pulsamos sobre el área negra aparece.

hola mundo visual basic


No hay comentarios:

Publicar un comentario