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.
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.
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
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.
Podemos ver un esquema de las clases creadas
También podemos consultar el visor de clases
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.
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.
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.
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
Y si
pulsamos sobre el área negra aparece.
No hay comentarios:
Publicar un comentario