Está en la página 1de 33

Introduccin:

La programacin orientada a objetos (POO) nos permite escribir cdigo menos


propenso a fallos adems de permitirnos la reutilizacin de cdigo de forma
ms conveniente.
En este artculo veremos las caractersticas de la POO desde el punto de vista
de los lenguajes de .NET Framework y cmo utilizar los distintos elementos
que nos permitirn crear cdigo que sea ms fcil de escribir y mantener.

LA PROGRAMACIN ORIENTADA A OBJETOS


En Todo Programacin existe una seccin denominada Cuadernos de
Principiantes donde se estudia algoritmia y estructuras de datos a nivel
iniciacin. Est planificado que se estudie a nivel terico la programacin
orientada a objetos, por tanto para aquellos que no tengis nocin alguna
sobre POO mejor guarda a buen recaudo este nmero de TP y espera a
aprender los conceptos tericos necesarios para luego aplicarlos en el marco,
nunca mejor dicho, de .NET.

LOS PILARES DE LA POO


Recordemos que tres son las principales caractersticas de un lenguaje
orientado a objetos, es decir, se considera que un lenguaje est totalmente
orientado a objetos si es capaz de proveer estas tres caractersticas:

Encapsulacin

Herencia

Polimorfismo

Veamos una pequea descripcin de cada una de ellas y despus las


ampliaremos para comprender mejor su significado y cmo puede
ayudarnos a crear aplicaciones que aprovechen todas las posibilidades que
nos da la POO.
La ENCAPSULACIN es la cualidad de unificar los datos y la forma de
manipularlos, de esta forma podemos ocultar el funcionamiento de una
clase y exponer solo los datos que manipula (mediante propiedades), as
como proveer de medios para poder manipular dichos datos (mediante
mtodos). De esta forma solo exponemos al mundo exterior la informacin

y la forma de manipularla, ocultando los detalles usados para manejar


esos datos y, lo que es ms importante, evitando que nadie manipule de
una forma no controlada dicha informacin.
La HERENCIA es la cualidad de poder crear nuevas clases (o tipos)
basadas en otras clases, de forma que la nueva clase obtenga todas las
caractersticas de la clase que ha heredado, tanto los datos que contiene
como la forma de manipularlos, pudiendo aadir nuevas caractersticas e
incluso cambiar el comportamiento de algunas de las incluidas en la clase
base, (siempre que as se haya previsto). Mediante la herencia podemos
crear de forma fcil una jerarqua de clases que comparten un mismo
comportamiento bsico pero que cada nueva generacin puede tener (y de
hecho tiene) un nuevo comportamiento.
El POLIMORFISMO es la cualidad de implementar de forma particular
algunas de las caractersticas que tienen las clases, de forma que cuando
necesitemos usarlas no nos preocupe la implementacin interna que cada
una tenga, lo que realmente nos interesa o nos debe importar es que
podemos usar esas caractersticas e incluso podamos acceder a ellas de
forma annima... o casi.

OTROS CONCEPTOS DE LA POO


Tal como tendrs oportunidad de ver en los Cuadernos de Principiantes y lo
indicado en el cuadro Los pilares de la POO, la POO se basa en tres
caractersticas que son comunes a todos los lenguajes orientados a objetos,
pero si tenemos esas caractersticas y no sabemos cmo aplicarlas, la verdad
es que no nos ser de mucha utilidad.
Pero antes de ver algo de cdigo concreto, creo que es importante que
aprendamos otros conceptos relacionados tambin con la POO, pero esta vez
desde un punto de vista del programador, es decir, vamos a dejar en parte la
teora y vamos a ser algo ms prcticos, ya que los siguientes conceptos sern
con los que tendremos que "bregar" a diario. Adems nos interesa conocerlos
para aprovechar lo que un lenguaje de programacin orientado a objetos nos
ofrece, si bien, es posible que, al menos de forma genrica, no todos los
lenguajes dispongan de ellos.
Por eso, aunque lo que se ha dicho y se diga a continuacin ser vlido para
cualquier lenguaje orientado a objetos, lo vamos a enfocar desde el punto de
vista de .NET Framework, ms concretamente desde el punto de vista del
programador de Visual Basic .NET y C#.

LAS CLASES Y ESTRUCTURAS

Como hemos estado mencionando, en los lenguajes orientados a objetos,


existe el concepto clase. Cuando hablamos de clases, tambin podemos
extenderlo a estructuras, de hecho, para los programadores de C++ una clase
no es ms que una estructura que se comporta de forma diferente.
Una clase es una pieza de cdigo en la que podemos definir una serie de datos
y al mismo tiempo unos mtodos (funciones o procedimientos) que nos
permitirn acceder a esos datos.
Cuando definimos una clase, lo que estamos haciendo es definir una plantilla, a
partir de la cual podemos crear objetos en la memoria. Por tanto, la clase es el
molde con el cual podemos crear nuevos objetos. Para poder crear algo
"tangible" a partir de una clase, tenemos que crear en la memoria un nuevo
objeto del tipo de la clase, en estos casos lo que decimos es que instanciamos
un nuevo objeto de la clase. A partir de ese momento tendremos algo real con
lo que podemos trabajar: una instancia de la clase, es decir, la definicin
realizada en la clase se ha convertido en un objeto al que podemos acceder y
que podemos empezar a utilizar, dndole nuevos valores a los datos que
manipula y usando las funciones que nos permiten manipular dichos datos.
La diferencia principal entre una clase y una estructura es la forma en que se
crean los objetos que representan a esas "ideas". Los objetos creados a partir
de las clases son objetos por referencia, es decir, si declaramos una variable
para manipular ese objeto, lo que tendremos ser una referencia (o puntero) a
una direccin de memoria en la que realmente est el objeto. Mientras que los
objetos creados a partir de una estructura se almacenan de forma diferente, en
lugar de "apuntar" a una direccin de memoria en la que se encuentra el
objeto, es como si las variables declaradas como estructuras fuesen realmente
el objeto permitindonos hacer ciertas operaciones y manipulaciones que los
objetos obtenidos a partir de una clase no pueden realizar de la misma forma.
Esto lo veremos despus con ms detalle.

NOTA: Clases
En .NET siempre usamos una clase para escribir cualquier tipo
de cdigo. Por tanto, hagamos lo que hagamos en .NET
Framework, debemos hacerlo dentro de una clase. Esto no
quiere decir que siempre tengamos que usar las caractersticas
de la POO, ya que si simplemente queremos hacer una
aplicacin que muestre un mensaje en la consola, el cdigo no
tiene porqu usar la herencia, el polimorfismo o la
encapsulacin, simplemente escribimos el cdigo que muestre el
mensaje y asunto arreglado, pero lo que si podremos hacer es
usar algunas de las "otras" ventajas que nos aporta la
programacin orienta a objetos.

INTERFACES
Cuando hablamos de polimorfismo, ineludiblemente tenemos que hablar de las
interfaces, ya que, principalmente, nos posibilita utilizar esta caracterstica de
la POO. La pregunta es: qu es una interfaz? Aqu no hablamos de "interfaces
de usuario", es decir, lo que se mostrar al usuario de nuestra aplicacin, sino
a una clase especial en la que solamente se definen los mtodos y propiedades
que una clase que la implemente debe codificar. Las interfaces representan un
contrato, de forma que cualquier clase que la implemente debe utilizar los
miembros de la interfaz usando la misma forma en que sta la ha descrito:
mismo nmero de argumentos, mismo tipo de datos devuelto, etc.
Gracias a la implementacin de interfaces podemos crear relaciones entre
clases que no estn derivadas de la misma clase base, pero que tengan
mtodos comunes, al menos en la forma, aunque no necesariamente en el
fondo. Anteriormente usamos el ejemplo del mtodo Guardar, este mtodo se
puede definir en una interfaz, las clases que quieran implementar un mtodo
Guardar "estandarizado" firmarn un contrato con la interfaz que lo especifica,
aunque la forma interna de funcionamiento solo atae al programador de la
clase, lo importante es saber que cualquier clase que haya firmado ese
contrato tendr que seguir las condiciones impuestas por la interfaz, de esta
forma todas las clases tendrn un mtodo Guardar "compatible", aunque, tal
como mostramos antes, cmo se realice esa accin de guardar no debe
preocuparnos, simplemente nos fiaremos de que se ha implementado
adecuadamente para almacenar los datos que la clase manipula.

NOTA: HERENCIA MLTIPLE Y HERENCIA SIMPLE


En C++ y algunos otros lenguajes orientados a objetos se
permiten la herencia mltiple, es decir, una clase se puede
derivar de varias clases a la vez. Los lenguajes de .NET
Framework, usan lo que se denomina herencia simple, es decir,
una clase solo se puede derivarse directamente de otra clase, si
bien se permite implementar mltiples interfaces.
Pero debido a cmo funciona la herencia, cualquier clase
derivada a partir de otra, heredar indirectamente todas las
clases e interfaces que la clase base haya heredado o declarado.
Adems, en .NET, todas las clases siempre se derivan de la clase
base Object que es la clase que est en la parte superior de la
jerarqua de clases.

CONSTRUCTORES Y DESTRUCTORES, EL PUNTO DE


INICIO Y FINAL DE LAS CLASES

Cuando creamos un objeto a partir de una clase, se sigue un proceso, el cual


empieza en el momento en que decidimos crear una nueva instancia de dicha
clase.
En estos casos, el compilador utiliza lo que se llama el constructor de la clase.
Siempre que se crea un nuevo objeto en la memoria est involucrado el
constructor de la clase.
Los constructores son procedimientos especiales (funciones que no devuelven
un valor) en los que podemos escribir toda la lgica que debe usarse para la
correcta creacin del objeto. Por ejemplo, podemos inicializar las variables
usadas, podemos asignarle algunos valores predeterminados, etc.
De igual forma, cuando un objeto ya no se necesita ms, se destruye mediante
una llamada al destructor de la clase. En .NET la destruccin de los objetos
suele hacerse de forma automatizada, es decir, a diferencia de lo que ocurre en
otros entornos de programacin, no es necesario destruir explcitamente un
objeto para eliminarlo de la memoria, esa gestin de limpieza de objetos la
realiza el recolector de basura (Garbage Collector, GC) de .NET, el cual decide
cuando un objeto no se necesita ms y en ese caso lo elimina dejando libre la
memoria utilizada para otros menesteres.

SOBRECARGA (OVERLOAD)
Una de las caractersticas que tambin nos ofrece los lenguajes orientados a
objetos es la posibilidad de definir varias funciones de las clases con un mismo
nombre, de esta forma, podremos crear versiones diferentes, por ejemplo para
que reciban argumentos de distintos tipos sin necesidad de cambiarle el
nombre.
Supongamos que queremos hacer una funcin que realice cualquier tipo de
operacin sobre dos valores numricos, sera lgico pensar que si esos valores
son de tipo entero, el resultado que devuelva la funcin tambin debera ser de
tipo entero, en caso de que los valores a usar en la operacin son de tipo
flotante, el resultado podra devolverlo de ese mismo tipo.
En los lenguajes no orientado a objetos, tendramos que crear dos funciones
con nombres diferentes, por ejemplo: sumaInt y sumaFloat. Pero la
sobrecarga nos permite crear dos funciones que se llamen suma y el
compilador utilizar la adecuada segn el tipo de datos que pasemos como
argumentos.
El nico requisito para poder crear sobrecargas de mtodos es que las
diferentes versiones se diferencien en los argumentos, ya sea porque sean de
diferentes tipos de datos o porque el nmero de argumentos usados sea
diferente, de esa forma el compilador no tendr ningn problema en saber cual
debe usar en cada ocasin. La sobrecarga la podemos aplicar tanto a los
constructores como a cualquier otro mtodo de la clase.

NOTA: Sobrecarga
No existir la posibilidad de crear mtodos sobrecargados si
solamente se diferencian en el tipo de datos devuelto, ya que en
esos casos el compilador no podr decidir correctamente qu
mtodo debe utilizar.

LOS MIEMBROS DE LAS CLASES: CAMPOS,


PROPIEDADES Y MTODOS
Como hemos comentado, las clases manejan datos y proveen de funciones
para acceder a esos datos. Para ser precisos, los datos se mantienen o
almacenan internamente en los campos declarados en las clases. Los campos
no son otra cosa que variables declaradas en la clase, habitualmente
declaradas de forma privada. Por qu declaradas de forma privada?
Precisamente para seguir o cumplir la caracterstica de encapsulacin de la
POO, es decir, los datos no deben exponerse de forma directa.
Si queremos exponer los datos, podemos usar las propiedades. Las
propiedades son funciones especiales que nos permiten acceder a esos datos,
aunque para ser ms precisos, las propiedades realmente representan a los
datos que una clase contiene, al menos de forma pblica. De esa forma
podemos "controlar" la forma en que se leen o asignan esos datos, ya que las
propiedades realmente son funciones en las que podemos escribir cdigo para
controlar los valores asignados o ledos.
Los mtodos nos permitirn realizar acciones sobre los datos, por ejemplo
devolver un rango de valores o simplemente una representacin amigable de la
informacin contenida. Debido a que algunas veces los mtodos devolvern
algo y otras no, podemos usar tanto funciones que devuelvan o no un valor.

NOTA: Mtodos
En C# los mtodos siempre son funciones, que devolvern un
tipo concreto o el valor especial void, que se usa para indicar
que una funcin no devolver ningn valor.
En Visual Basic .NET existen dos tipos de mtodos distintos, las
funciones (Function) que siempre devuelven un valor y los
procedimientos (Sub) que no devuelven ningn valor.

Adems de los campos, mtodos y propiedades, las clases tienen otros


miembros como los eventos y las enumeraciones. stos nos permitirn recibir
notificaciones de cuando algo ocurra (eventos) o declarar ciertos valores
constantes que podemos usar para restringir algunos valores asignados a las
propiedades o que nos permitan seleccionar de forma coherente la informacin
que queremos obtener (enumeraciones).

EL MBITO DE LOS MIEMBROS DE LAS CLASES


Las buenas formas de trabajar con las clases nos indican que los campos
deberan ser privados, con idea de que no estn accesibles de forma externa.
Por supuesto tambin podemos definir otros miembros de las clases de forma
privada, esto es til cuando la funcionalidad es para uso exclusivo de otros
miembros de la clase. Pero cuando queremos exponer la funcionalidad fuera de
la clase podemos hacerla de varias formas, aqu es donde entran en juego el
mbito de los miembros de las clases.
El mbito lo aplicamos para permitir el acceso desde cualquier cdigo fuera de
la clase o para restringir ese acceso. Dependiendo de cmo queramos que se
acceda a los miembros de la clase podemos usar distintos modificadores de
mbito.
Veamos los que podemos usar y cuando y porqu usarlos.
La instruccin entre parntesis ser la que tendremos que usar en C#.

Private (private). Para uso privado. Cuando declaramos un miembro


como privado slo lo podremos acceder desde la propia clase. Este es el
ms restrictivo y el que se recomienda para los campos y las funciones
de uso interno.

Protected (protected). Uso protegido. Los elementos declarados


como protegidos slo estarn accesibles, adems de en la propia clase,
por cualquier clase derivada.

Friend (internal). Para uso dentro de la propia aplicacin. Cuando


declaramos un miembro con este modificador, solo podremos acceder a
l desde la propia clase o desde cualquier cdigo que se encuentre en el
mismo ensamblado (proyecto).

Protected Friend (protected internal). Una mezcla de Protected y


Friend, es decir solo accesible desde las clases derivadas o desde el
mismo proyecto.

Public (public). Este modificador de mbito nos permite exponer


pblicamente cualquier miembro de la clase, de forma que no haya
restricciones para acceder a l.

NOTA: mbito
Los miembros de una clase los podemos declarar sin especificar
el mbito, dependiendo del lenguaje de programacin que
usemos se aplicar un modificador de mbito u otro. En C#, si
no indicamos el mbito, las declaraciones se consideran
privadas, mientras que en Visual Basic .NET el mbito
predeterminado es Friend.

MIEMBROS VIRTUALES, NO REEMPLAZABLES Y


ABSTRACTOS
Para ir terminando la parte "terica" sobre la programacin orientada a
objetos, veamos cmo podemos darle un significado distinto a los miembros de
una clase, dependiendo de cmo queramos que se comporten y por extensin
cmo podemos utilizarlos tanto en la propia clase como en las clases
derivadas.
Como hemos comentado, cuando una clase hereda a otra podemos modificar el
comportamiento de los miembros heredados, pero estos solamente se podrn
modificar si la clase base as lo contempla o lo permite. De forma
predeterminada, al menos en .NET, cuando declaramos un mtodo o una
propiedad en una clase, solo podremos acceder a l desde una instancia creada
(un objeto) en memoria, desde donde podemos usarlos depender del mbito
que le hayamos aplicado. De igual forma, el que una clase que se base en otra,
pueda crear su propia versin de ese mtodo o propiedad depender de que la
clase base lo haya declarado como virtual (Overridable en VB .NET). Los
mtodos virtuales sern los que podamos sobrescribir en las clases derivadas,
de forma que podamos crear nuestras propias versiones. En .NET los miembros
de una clase no son virtuales de forma predeterminada. Por tanto, si queremos
que la clase derivada pueda crear su propia versin de un mtodo, debemos
declararlo como virtual o "redefinible".

Si en una clase base hemos definido un mtodo virtual, pero posteriormente


queremos que no se pueda seguir redefiniendo en otras clases derivadas,
debemos indicarlo usando el modificador NotOverridable, el cual se usar
junto con Overrides, ya que sobrescribe un miembro de la clase base y como
adems lo queremos marcar como no virtual, debemos usar las dos
instrucciones: Overrides NotOverridable, (en C# se indicar con override
sealed).
Pero tambin se nos puede presentar el caso contrario, en el que queremos
que un mtodo forzosamente haya que redefinirlo en las clases derivadas, en
esos casos la clase base que lo define no incluye ninguna implementacin, es
decir, el mtodo no contiene cdigo ejecutable, solo la definicin, (como ocurre
con las interfaces). Se dice que estos mtodos son abstractos porque solo se
ha definido en la forma y no se ha implementado ningn cdigo ejecutable. En
Visual Basic se definen usando el modificador MustOverride (asbtract en
C#). Estos mtodos abstractos solo se pueden declarar en clases abstractas
(MustInherit en Visual Basic, abstract en C#) y por la necesidad de tener
que redefinirlos, son implcitamente virtuales.
Las instrucciones o modificadores que nos permiten crear estos tipos de
miembros son:

Overridable (virtual). Los miembros virtuales son los que las clases
derivadas puedes sobrescribir para crear su propia versin. Para indicar
en una clase derivada que estamos sobrescribiendo dicho elemento,
usaremos Overrides (override en C#).

NotOverridable (sealed). Los miembros virtuales heredados se


pueden marcar como no virtuales, (las siguientes clases derivadas no
podrn sobrescribirlos), usando esta instruccin a continuacin de
Overrides (override).

MustOverride (abstract). Un mtodo que se debe reemplazar en la


clase derivada y que solo se define en la clase base sin ningn cdigo
ejecutable. Los mtodos abstractos son virtuales por defecto y solo se
pueden declarar en clases abstractas.

MIEMBROS DE INSTANCIAS Y COMPARTIDOS

En todos estos casos, los miembros de la clase siempre son miembros de


instancia, es decir, solo existen en la memoria cuando se crea un nuevo objeto
(se crea una nueva instancia). Pero es posible que nos interese crear miembros
compartidos, es decir, miembros que pertenecen a la propia clase, no a
ninguna instancia en particular. Dndonos la oportunidad de poder acceder
siempre a ellos, independientemente de que hayamos creado o no un nuevo
objeto en la memoria. En estos casos decimos que creamos miembros
compartidos (estticos en el argot de C#/C++), esta diferencia de
"nomenclatura" dependiendo del lenguaje de programacin, es porque para
definir un miembro perteneciente a la clase y no a una instancia en particular,
usaremos en Visual Basic la instruccin Shared (compartido), mientras que en
C# se usar la instruccin static (esttico).
Resumiendo, Shared (static), declara un miembro compartido, los miembros
compartidos no pertenecen a ninguna instancia en particular y solamente
pueden acceder a campos u otros miembros tambin compartidos. Desde los
miembros de instancia podemos acceder tanto a miembros compartidos como
de instancia.

NOTA: STATIC
En Visual Basic existe tambin la instruccin Static, (que no
tiene equivalencia en C#), en este caso se utiliza con variables
declaradas en un procedimiento y sirven para indicar que esa
variable debe mantener el valor entre distintas llamadas a dicho
procedimiento, a diferencia del resto de variables que solo
existen mientras se ejecuta el cdigo del procedimiento y cuyos
valores se pierden al finaliza la ejecucin del mismo.

CLASES ABSTRACTAS Y SELLADAS


De igual forma que podemos modificar el comportamiento de los miembros de
una clase, tambin podemos cambiar el comportamiento predeterminado de
las clases.
Como hemos comentado, las clases de .NET pueden usarse para crear nuevas
clases derivadas de ellas, esta es la funcionalidad predeterminada, pero no
obligatoria, es decir, si queremos podemos usar una clase por si misma o como
base de otras.
Pero tambin podemos hacer que una clase solamente se use como clase base
de otras, pero no se puedan usar para crear nuevas instancias en memoria,
este es el caso de las clases abstractas. Una clase abstracta puede contener
miembros abstractos, miembros normales o virtuales. Para indicar que una

clase es abstracta, se usa el modificador MustInherit en Visual Basic o


abstract en C#.
La contrapartida de las clases abstractas son las clases selladas o clases que no
se pueden usar como clases base, en estos casos las clases las definiremos
como NotInheritable en Visual Basic o sealed en C#. Como es lgico, las
clases no heredables se pueden usar en ocasiones en las que no nos interese
que nadie cambie el comportamiento que tiene, por tanto no se podrn
declarar miembros virtuales ni abstractos, ya que no tendra ningn sentido.
Las estructuras siempre son "clases selladas", (aunque no se use un
modificador para indicarlo), por tanto, no podemos usarlas como base de otras.

Conclusiones
En la segunda parte de esta serie dedicada a la programacin orientada a
objetos veremos cmo poner en prctica todo lo que hemos comentado,
adems de ver otras peculiaridades de la POO, tales como la definicin de
interfaces y cmo implementarlas, ocasin que tambin aprovecharemos para
ver de forma prctica cmo usar el polimorfismo y la herencia en los lenguajes
de .NET.

Introduccin:
En la entrega anterior vimos algunos conceptos tericos de la POO
(Programacin Orientada a Objetos) desde el punto de vista de los lenguajes
de .NET Framework, en esta ocasin veremos con ejemplos prcticos cmo
utilizar las caractersticas de la POO en Visual Basic .NET, (en el ZIP se incluye
tambin el cdigo para C#), de forma que tengamos claro cmo usar la
herencia, el polimorfismo y la encapsulacin, pero con cdigo.

VISUAL BASIC .NET Y LA POO


Tal como comentamos en el artculo anterior, uno de los pilares de la POO es la
herencia. Mediante la herencia podemos definir clases totalmente operativas y
crear nuevas clases basadas en ellas de forma que hereden toda la
funcionalidad de la clase base y nos permita ampliarla. Por tanto, vamos a
empezar viendo cmo definir una clase y cmo aplicar en Visual Basic .NET el
resto de conceptos relacionados con la programacin orientada a objetos.

Nota:
Este artculo se public en Octubre de 2004, y por tanto todo lo
aqu explicado est relacionado con la versin 1.1 de .NET
Framework.
La versin de .NET Framework que hay actualmente (a la hora
de escribir esta nota) es la 3.0, (que en el fondo es la misma
que la 2.0), y se han introducido ciertos cambios o mejoras,
pero bsicamente lo aqu explicado sigue siendo tan vlido
ahora como hace dos aos.

DEFINIR CLASES EN VISUAL BASIC .NET


Antes de poder usar las caractersticas de la POO tendremos primero que
aprender
a
declarar
una
clase.
La declaracin de una clase se define usando la instruccin Class seguida del
nombre de la clase y termina usando las instrucciones End Class.
Dentro de ese bloque definiremos los campos, propiedades y mtodos que
queramos que tenga la clase.

NOTA:
En Visual Basic .NET la definicin de una clase se puede hacer
en cualquier fichero de cdigo (con extensin .vb), aunque no
es obligatorio hacerlo en un fichero independiente como ocurra
con las versiones anteriores, es recomendable hacerlo, para que
nos resulte ms fcil de mantener.

Public Class A
Private _prop2 As Integer
Private _prop1 As String
'
Public Property Prop1() As String
Get
Return _prop1
End Get
Set(ByVal value As String)
If value <> "" Then

_prop1 = value
End If
End Set
End Property
Public Property Prop2() As Integer
Get
Return _prop2
End Get
Set(ByVal value As Integer)
_prop2 = value
End Set
End Property
'
Public Sub Mostrar()
Console.WriteLine("{0}, {1}", _prop1, _prop2)
End Sub
End Class

Listado 1

Tal como podemos ver en el listado 1, tenemos una clase llamada A que define
dos campos, dos propiedades y un mtodo. Los dos campos, declarados como
privados, se usan para mantener "internamente" la informacin que se expone
mediante las dos propiedades pblicas, de esta forma protegemos los datos y
esta sera una forma de encapsular la informacin.
De las dos propiedades definidas para acceder a esos datos, solo la propiedad
Prop1 hace una comprobacin de que no se asigne una cadena vaca al campo
que mantiene internamente la informacin, aunque en este ejemplo por su
simplicidad no hacemos ms comprobaciones, en una clase algo ms compleja,
se podran realizar otras comprobaciones, por ejemplo si el valor a almacenar
es una cuenta de email, podramos comprobar que es una cadena
correctamente formada.
Las propiedades suelen definir dos bloques de cdigo, uno, el bloque Get se
utiliza cuando queremos acceder al valor devuelto por la propiedad, el otro es
el bloque Set, el cual se utilizar cuando asignemos un valor a la propiedad.
El mtodo Mostrar se usar para mostrar el contenido de las dos propiedades
por la consola y est definido como Sub (void en C#) porque no devuelve
ningn valor.

SOBRESCRIBIR MIEMBROS HEREDADOS


Tal como comentamos en el artculo anterior, todas las clases de .NET se
derivan directa o indirectamente de la clase Object. La clase definida en el
listado 1 tambin se deriva de Object aunque no se indique expresamente.
La clase base de todas las clases de .NET tiene un mtodo que se utiliza para
recuperar informacin, (en formato cadena), del contenido de la clase: el
mtodo
ToString.
Cada clase puede crear su propia versin del mtodo ToString para que
devuelva una representacin adecuada, por ejemplo los tipos numricos
devuelven una cadena que representa al nmero que contiene.
En nuestra clase podemos redefinirlo para que nos devuelva el contenido de los
dos datos que la clase mantiene:
Public Overrides Function ToString() As String
Return String.Format("{0}, {1}", _prop1, _prop2)
End Function

Para indicarle al compilador que estamos redefiniendo un mtodo ya existente,


lo indicamos con la instruccin Overrides (override en C#). Debido a que
ahora nuestra clase tiene una nueva versin de este mtodo, cualquier clase
que se derive de ella tambin heredar la nueva implementacin del mtodo
ToString.

USAR LA HERENCIA EN VISUAL BASIC .NET


La clase que hemos definido en el listado 1 no indicaba de ninguna forma que
se deriva de la clase Object, este es un caso excepcional, ya que todas las
clases de .NET se derivan de forma "automtica" de la clase Object.
Para indicar en Visual Basic .NET que una clase se deriva de otra, debemos
usar la instruccin (o palabra clave) Inherits seguida de la clase que
queremos
usar
como
base.
Esa instruccin tiene que indicarse al principio de la declaracin de la clase,
antes que cualquier otra instruccin, con excepcin de los comentarios.
Para crear una clase que se derive de la clase A definida en el listado 1,
tendramos que hacer lo siguiente:
Public Class B
Inherits A
End Class

Tal como podemos comprobar, la clase B no define ningn mtodo ni


propiedad, pero realmente si que tiene mtodos y propiedades: todos los que
tenga la clase A (adems de los de la clase Object).
Para comprobarlo podemos definir una variable del tipo de la clase B y
comprobaremos que esta clase tiene los mismos miembros que la clase A, tal
como se muestra en el listado 2.

Sub Main()
Dim objB As New B
objB.Prop1 = "guille"
objB.Prop2 = 47
objB.Mostrar()
Console.WriteLine("{0}", objB.ToString)
End Sub

Listado 2

OCULTAR MIEMBROS HEREDADOS


Todo funciona como esperbamos, aunque hay un pequeo problema, si
quisiramos modificar el comportamiento de los miembros heredados por la
clase B, no podramos hacerlo. La razn es bien simple: la clase A no ha
definido los miembros como virtuales (o reemplazables). Por tanto no se
pueden crear nuevas versiones de los mismos en la clase B, o casi...
Realmente la clase B si que puede definir nuevas versiones de los miembros
que tiene la clase A, al menos puede crear mtodos y propiedades que tengan
el mismo nombre, por ejemplo:
Public Sub Mostrar()
Console.WriteLine("Mostrar en la clase B: {0}, {1}", Prop1,
Prop2)
End Sub

Esto es totalmente correcto, al menos en el sentido de que no produce ningn


error; lo ms que producir esa declaracin es una advertencia del compilador
indicndonos que ese mtodo entra en conflicto con el definido en la clase base
A, tal como podemos comprobar en la figura 1.

Figura 1. Advertencia de ocultacin

Esa advertencia nos informa que deberamos indicar que la declaracin "oculta"
a la definida en la clase A y por tanto deberamos usar la instruccin Shadows
(new en C#). Aunque usemos Shadows, el problema real sigue existiendo: el
mtodo declarado en la clase B oculta al declarado (y heredado) en la clase A.
Si despus de definir este mtodo de la clase B volvemos a ejecutar el cdigo
del listado 2, comprobaremos que se utiliza el nuevo mtodo.
Posiblemente el lector pensar que eso es lo que queramos conseguir: tener
nuestra propia versin del mtodo Mostrar. Es ms, si definimos una nueva
clase que se derive de B podemos comprobar que realmente es ese mtodo el
que
se
hereda
por
la
nueva
clase.
En el listado 3 podemos ver la definicin de la clase C y el cdigo para
comprobar lo que mostrara:

Public Class C
Inherits B
End Class
Sub Main()
Dim objC As New C
objC.Prop1 = "guille"
objC.Prop2 = 47
objC.Mostrar()
Console.WriteLine("{0}", objC.ToString)
End Sub

Listado 3

Y lo que imprime es exactamente lo mismo que usando la clase B. Por tanto, el


objetivo est conseguido, es decir, la clase B ha "redefinido" un mtodo de la
clase A y esa nueva versin es la que se usar a partir de ese momento por las
clases que se basen en la clase B.
Aparentemente as es. Al menos si lo tomamos al pie de la letra.
El nico problema es que acabamos de romper una de las cualidades de la
programacin orientada a objetos: el polimorfismo.
Esta nueva definicin del mtodo Mostrar ya no tiene nada que ver con la
definida por la clase A y por tanto no existe ninguna relacin "polimrfica"
entre ambos mtodos.
Para ser ms precisos, tanto la clase B como la clase C tienen dos definiciones
del mtodo Mostrar: el inicialmente heredado de la clase A y el nuevo definido
por la clase B, aunque siempre prevalecer el definido expresamente en la
clase derivada frente al heredado de la clase base.
Si pudisemos ver el objeto creado en la memoria a partir de la clase B (e
incluso de la clase C), nos daramos cuenta de que realmente est dividido en
tres partes, tal como se muestra en la figura 2:
1. La parte heredada de la clase Object
2. La parte heredada de la clase A
3. Las definiciones propias de la clase B

Figura 2. Objeto B en memoria

El mtodo ToString definido en Object ha sido reemplazado o, para que lo


comprendamos mejor, sustituido por el redefinido en la clase A, pero el mtodo
Mostrar de la clase A an existe, lo que ocurre es que ha sido ocultado por el
que
se
ha
definido
en
la
clase
B.
Para demostrar que es as, que existen dos mtodos Mostrar en la memoria,
podemos utilizar el polimorfismo para "extraer" el mtodo Mostrar que est
oculto.
Para ello tendremos que declarar una variable del tipo de la clase A y decirle
que extraiga del objeto B la parte que le corresponde: solo la parte definida en
la
clase
A.
Esto se consigue de una forma muy simple: asignando a una variable del tipo
A el contenido de la variable que apunta al objeto B:
Dim objA As A
objA = objB

A partir de este momento el objeto B est siendo referenciado por el objeto A,


pero, y esto es importante, solo la parte que A conoce, es decir, la variable
objA solamente podr acceder al "trozo" del objeto B que se deriv de la clase
A.
Por tanto, si llamamos al mtodo Mostrar de la variable objA, accederemos al
mtodo Mostrar que el objeto B contiene porque la consigui al derivarse de
A.

S, esto es algo complicado y que no es fcil de comprender, pero es


importante intentar asimilarlo, ya que es muy probable que lo necesitemos en
nuestros proyectos, sobre todo si sabemos que se puede hacer.
Adems de que esta sera la nica forma que tenemos de acceder a ese
mtodo "oculto", ya que cualquier intento de acceder al mtodo Mostrar
mediante un objeto del tipo B, siempre acceder al definido en la propia clase
B y que oculta al heredado de la clase A.

NOTA:
Cuando declaramos una variable y la instanciamos, (creando un
nuevo objeto a partir de una clase), dicha variable simplemente
tiene una referencia al objeto creado en la memoria, (la variable
simplemente tiene un puntero al objeto real), por tanto, ese
objeto existe y puede ser referenciado por otras variables,
aunque esas otras variables deben ser de tipos "incluidos" en la
clase usada para crear dicho objeto.
Al instanciar un nuevo objeto del tipo B, (tal como se muestra
en la figura 2), podemos acceder a l mediante variables de tipo
Object, de tipo A y, por supuesto, de tipo B.

INDICAR LOS MIEMBROS VIRTUALES


Como hemos comprobado, si queremos que los miembros de nuestra clase se
puedan redefinir en clases derivadas, debemos indicarlo de forma expresa.
Para que esto sea as, utilizaremos la instruccin Overridable (virtual en C#).
De esta forma le indicaremos al compilador que el mtodo se puede redefinir,
es decir, que en la clase derivada se puede crear una nueva versin
"personalizada"
de
dicho
miembro.
Con esto logramos que en sucesivas derivaciones de la clase solamente exista
un
mismo
miembro
polimrfico.
La ventaja principal es que si en otra clase decidimos crear una nueva versin
de, por ejemplo, un mtodo, cuando se cree un objeto en la memoria, solo
existir ese mtodo, no varios mtodos con el mismo nombre, pero sin
ninguna relacin entre ellos, exceptuando el hecho de que se llamen de la
misma forma.
Tal como vimos en la seccin SOBRESCRIBIR MIEMBROS HEREDADOS,
tendremos que usar la instruccin Overrides (override en C#), para indicar
que nuestra intencin es crear una versin propia de uno de los miembros
heredados.
Pero no solo basta con usar esa instruccin, ya que si queremos redefinir un
miembro existente en alguna de las clases que hemos heredado, la "firma" de
nuestra versin debe ser la misma que el original. Es decir, si es un mtodo

que devuelve una cadena y no recibe parmetros, debemos "respetar" esas


mismas caractersticas, porque de lo que se trata es de cumplir con un
contrato, dicho contrato estipula que tipo de miembro es, (un mtodo o una
propiedad), que tipo de datos devuelve, si recibe o no parmetros y en caso de
que
los
reciba
de
que
tipo
deben
ser.
Todo esto es para garantizar que esos miembros se puedan usar de forma
independiente de la clase en el que se ha declarado, con idea de que podamos
acceder a ellos mediante objetos de las clases que se han usado para crear la
nueva, (las clases de las que se deriva la clase que sobrescribe el mtodo).
Por ejemplo, si el mtodo Mostrar definido en la clase A se hubiese declarado
como virtual, (Overridable), y en la clase B lo hubisemos redefinido usando
la instruccin Overrides, podramos acceder a dicho mtodo (de un objeto
creado a partir de la clase B) usando tanto una variable de la clase A como una
de la clase B; ya que en la memoria solamente existir un mtodo llamado
Mostrar. A diferencia de lo que ocurra antes (al no declararlo como virtual),
que realmente existan dos mtodos Mostrar.

DEFINIENDO INTERFACES
Una interfaz realmente es la definicin de los miembros pblicos de una clase.
Pero en los lenguajes de programacin de .NET tambin podemos definir clases
especiales que simplemente definan cmo deben ser los miembros que una
clase implemente. Es decir que caractersticas deben tener. De esta forma
podemos garantizar que si varias clases implementan los miembros definidos
en una interfaz, podemos usarlos de manera annima, es decir, sin necesidad
de saber si estamos usando un objeto de una clase o de otra, ya que si ambas
clases implementan la interfaz, tendremos la certeza de que dichas clases
tienen los miembros definidos en dicha interfaz.
Una interfaz representa un contrato, si una clase implementa una interfaz, est
suscribiendo dicho contrato, es ms, est obligada a cumplirlo, por tanto, la
clase tendr que definir todos los miembros que la interfaz contenga.
Antes de ver cmo usar las interfaces en nuestras clases, veamos cmo definir
una interfaz.
Public Interface IPrueba2
Property Prop1() As String
Sub Mostrar()
End Interface

En este caso hemos definido una interfaz llamada IPrueba2 (por convencin
los nombres de las interfaces siempre empiezan con la letra I mayscula), en
la
que
se
define
una
propiedad
y
un
mtodo.
Los miembros de las interfaces siempre son pblicos y no deben implementar
cdigo, solamente la definicin propiamente dicha.

UTILIZAR INTERFACES EN LAS CLASES


Cualquier clase que quiera disponer de los miembros definidos en la interfaz
debe indicarlo de forma expresa, ya que no solo es suficiente con definir los
miembros con nombres y caractersticas similares.
Para "implementar" en una clase los miembros definidos en una interfaz
tendremos que usar la instruccin Implements seguida del nombre de la
interfaz. Adems tendremos que definir los mtodos y propiedades que dicha
interfaz contiene, aunque en Visual Basic adems hay que indicarlo
expresamente, de forma que se sepa con seguridad de que cada uno de esos
miembros equivale a los definidos en la interfaz.
En el listado 4 vemos cmo definir una clase que utilice la interfaz que
acabamos de ver en la seccin anterior.

Public Class Prueba2


Implements IPrueba2
Public Sub Mostrar() _
Implements IPrueba2.Mostrar
' nuestra version del mtodo Mostrar
End Sub
Public Property Prop1() As String _
Implements IPrueba2.Prop1
Get
' el cdigo que devuelve
' el valor de la propiedad
End Get
Set(ByVal value As String)
' el cdigo que asigna
' el valor de la propiedad
End Set
End Property
End Class

Listado 4

El mtodo y las dos propiedades deben tener el mismo nombre y parmetros


(si
los
hubiera)
que
los
definidos
en
la
interfaz.
Cuando trabajamos con Visual Basic adems debemos indicar expresamente
que dicho mtodo o propiedad est "ligado" con el definido en la interfaz,
cuando
trabajamos
con
C#
no
es
necesario
indicarlo.
La ventaja de esta "redundancia" de VB es que podemos dar un nombre
diferente al miembro implementado, pero "internamente" el compilador sabr
que nos estamos refiriendo al que implementa la interfaz.
Tal como podemos ver en el listado 5, a pesar de que en la clase Prueba2B al
mtodo le hemos dado otro nombre, realmente est haciendo referencia al que
se ha declarado en la interfaz.

Public Class Prueba2B


Implements IPrueba2
Public Sub OtroNombre() _
Implements IPrueba2.Mostrar
Console.WriteLine("Este es el mtodo Mostrar de la clase
Prueba2B")
End Sub
Public Property Prop1() As String _
Implements IPrueba2.Prop1
Get
Return "Prop1 de la clase prueba2B"
End Get
Set(ByVal value As String)
'
End Set
End Property
End Class
Dim p2 As New Prueba2
Dim p2b As New Prueba2B
Dim i As IPrueba2
'
i = p2
i.Mostrar()
'
i = p2b

i.Mostrar()

Listado 5

POLIMORFISMO USANDO CLASES E INTERFACES


En los ejemplos mostrados ya hemos visto cmo usar el polimorfismo tanto a
travs de variables incluidas en las clases como con interfaces que dichas
clases implementan; vamos a detallar un poco ms, ya que esta es una de las
caractersticas ms usadas en .NET, por la sencilla razn de que muchas clases
de .NET Framework implementan interfaces de forma que podamos acceder a
los miembros implementados por medio de variables del tipo de dichas
interfaces.
Es ms, muchas de las clases de .NET adems permiten que demos nueva
funcionalidad a nuestras propias clases si implementamos ciertas interfaces.
Por ejemplo, si queremos que nuestra clase sea "clasificable", es decir, que se
pueda usar en una coleccin que clasifique los elementos que contiene, nuestra
clase
debe
implementar
la
interfaz
IComparable.
Si definimos una clase en la que queremos que el mtodo ToString acte de
forma que podamos especificar ciertos formatos a la hora de mostrar el
contenido,
nuestra
clase
debe
implementar
IFormattable.
Que queremos que nuestra clase acte como una coleccin, en la que se pueda
enumerar o recorrer el contenido de la misma, debemos implementar la
interfaz IEnumerable.
Pero esas interfaces propias del .NET Framework lo que harn ser darle una
nueva funcionalidad a nuestras clases, por tanto, lo importante es saber de que
forma actan los objetos creados en la memoria.
Tal como hemos comentado antes, solo existe un objeto en la memoria y
cuando accedemos a l lo podemos hacer bien usando alguna de las clases de
las que se deriva o bien mediante alguna de las interfaces que implementa.
Cuando accedemos a dicho objeto mediante algunas de estas clases o
interfaces simplemente estamos accediendo a la parte "conocida" por dicho
tipo.
En el cdigo del listado 4 y 5 la interfaz IPrueba2 "sabe" cmo acceder al
mtodo Mostrar y a la propiedad Prop1, independientemente del objeto que
la haya implementado, por tanto si una clase implementa dicha interfaz
podemos acceder a esos miembros mediante una variable del tipo IPrueba2,
tal como se demuestra en el listado 5 en el que accedemos al mtodo Mostrar
definido en la interfaz e implementado por las dos clases.

USAR EL POLIMORFISMO PARA ACCEDER A ELEMENTOS


DIFERENTES DE UN ARRAY O COLECCIN

Una de las utilidades del polimorfismo (de clases o interfaces) es que podemos
crear arrays de variables de un tipo "bsico" y en ese array incluir objetos que
si bien son distintos, en el fondo tienen como parte componente la clase de la
que
se
ha
declarado
el
array.
El ejemplo ms bsico y vlido tanto para las clases declaradas en el propio
.NET Framework como las declaradas por nosotros mismos, es crear un array
de tipo Object, dicho array podr contener objetos de cualquier tipo (incluso
tipos como nmeros enteros, cadenas, etc.), y posteriormente poder acceder a
cualquiera de los elementos mediante un objeto del tipo Object, en cuyo caso
solo podremos acceder a los mtodos que Object implementa o bien mediante
objetos de un tipo en particular, en cuyo caso nos veremos obligados a hacer
una conversin desde el tipo contenido en el array (Object) al tipo particular
que nos interese.
En el listado 6 podemos ver un ejemplo en el que se crea un array de tipo
Object pero que se almacenan tanto objetos del tipo clase A, clase B,
IPrueba2, Integer y String.

Dim a(6) As Object


'
Dim a1 As New A
a1.Prop1 = "Objeto A"
Dim b1 As New B
b1.Prop1 = "Objeto B"
Dim c1 As New C
c1.Prop1 = "Objeto C"
'
a(0) = a1
a(1) = b1
a(2) = c1
a(3) = New Prueba2
a(4) = New Prueba2B
a(5) = 15
a(6) = "Hola"
'
Dim i As Integer
Console.Write("Usando el mtodo ToString")
Console.WriteLine(" de los objetos contenidos")
For i = 0 To a.Length - 1
Console.WriteLine("a({0}) = {1}", i, a(i).ToString())
Next
'

Console.WriteLine()
'
Console.WriteLine("Usando un Object")
Dim o As Object
For Each o In a
Console.WriteLine("o.ToString = {0}", o.ToString())
Next
'
Console.WriteLine()
'
Console.WriteLine("Usando tipos especficos")
For Each o In a
Console.WriteLine("El tipo es: {0}", o.GetType().Name)
If TypeOf o Is A Then
Dim tA As A = CType(o, A)
tA.Mostrar()
ElseIf TypeOf o Is IPrueba2 Then
Dim tIPrueba2 As IPrueba2 = CType(o, IPrueba2)
tIPrueba2.Mostrar()
ElseIf TypeOf o Is Integer Then
Dim tInt As Integer = CType(o, Integer)
Console.WriteLine(tInt.ToString("00000"))
ElseIf TypeOf o Is String Then
Dim tStr As String = o.ToString
Console.WriteLine(tStr)
Else
Console.WriteLine("o.ToString = {0}", o.ToString())
End If
Next

Listado 6

Tal como podemos comprobar en el ltimo bucle de dicho listado, se utiliza la


instruccin compuesta TypeOf ... Is para saber si un objeto es de un tipo
concreto (en C# usaramos is), tambin podemos ver que usando el mtodo
GetType podemos obtener el tipo subyacente as como el nombre de dicho
tipo.

Si nos fijamos, al hacer la comprobacin TypeOf o Is A aqu se procesarn


tanto los objetos del tipo A como los derivados de dicho tipo, lo mismo ocurre
con la interfaz IPrueba2.
Pero este ejemplo al ser genrico y usando la clase Object seguramente no
acabar de "cuajar", por tanto vamos a crear un ejemplo en el que crearemos
variables de un tipo concreto: Cliente y derivaremos un par de clases en las
que agregaremos nueva funcionalidad a la clase base, posteriormente
crearemos un array del tipo Cliente en el que podremos almacenar variables
de cualquiera de esos tipos derivados de ella.

Nota:
Por la extensin del listado, el mismo se incluye en el ZIP con el
cdigo de los ejemplos (tanto para Visual Basic como para C#),
en el listado 7 puedes ver cmo usar esas clases.

En dicho cdigo tendremos ocasin de ver cmo podemos implementar la


interfaz IComparable para que estas clases se puedan agregar a una
coleccin
y
posteriormente
clasificarlas.
Adems implementaremos la interfaz IFormattable para que, si los
mostramos por la consola o usamos el mtodo Format de la clase String,
podamos usar los siguientes formatos personalizados:

ANS mostrar los apellidos, el nombre y el saldo

NAS mostrar el nombre, los apellidos y el saldo

AN mostrar los apellidos y el nombre (predeterminado)

NA mostrar el nombre y los apellidos

S mostrar el saldo

Sub Main()
Dim acli(6) As Cliente
'
acli(0) = New Cliente("Jose", "Sanchez", 125.5D)
acli(1) = New ClienteOro("Luis", "Rebelde", 2500.75D)

acli(2) = New ClienteMoroso("Antonio", "Perez", -500.25D)


acli(3) = New Cliente("Miguel", "Rodriguez", 200)
acli(4) = New ClienteMoroso("Juan", "Ruiz", -310)
acli(5) = New ClienteOro("Mariano", "Alvarez", 500.33D)
acli(6) = New Cliente("Carlos", "Bueno", 975)
'
Console.WriteLine("Antes de clasificar:")
For Each c As Cliente In acli
Console.WriteLine("{0}, saldo= {1}", c, c.MostrarSaldo())
Next
Array.Sort(acli)
'
Console.WriteLine()
Console.WriteLine("Despus de clasificar:")
For Each c As Cliente In acli
Console.Write("{0}, saldo= {1}", c, c.MostrarSaldo())
If TypeOf c Is ClienteOro Then
Console.WriteLine(" -> $$$ es cliente ORO $$$")
ElseIf TypeOf c Is ClienteMoroso Then
Console.WriteLine(" -> OJO que es un cliente moroso")
Else
Console.WriteLine()
End If
Next
'
Console.WriteLine()
Console.WriteLine("Mostrar usando formatos:")
For Each c As Cliente In acli
Console.WriteLine("Usando NAS= {0:NAS}", c)
Console.WriteLine("Usando AN= {0:AN}", c)
Console.WriteLine("Usando S= {0:S}", c)
Next
'
Console.ReadLine()
End Sub

Listado 7

CONSTRUCTORES Y SOBRECARGA DE CONSTRUCTORES


El punto de inicio de cualquier clase, cuando se crea una instancia en la
memoria, es un mtodo especial al que se le conoce como constructor.
Un constructor no devuelve ningn valor, por tanto en Visual Basic sera un
mtodo de tipo Sub llamado New (en C# no se declara como void,
simplemente tendr el mismo nombre de la clase).
Los constructores tambin se pueden sobrecargar, es decir, pueden existir
varias versiones en las que cada una de ellas reciba distintos parmetros, en
nmero y/o en tipo.
Debido a que todas las clases (y estructuras) deben tener un constructor, si
nosotros no lo definimos de forma expresa, ser el propio compilador el que se
encargue de aadirlo por nosotros, en ese caso ser un constructor en el que
no
se
reciba
ningn
argumento.
Aunque hay que tener presente que en el momento en que hemos definido un
constructor el compilador ya no agregar ninguno de forma automtica. Esto
tiene sus ventajas, ya que en ocasiones es posible que nos interese que
nuestras clases solamente se puedan instanciar si se le pasa algunos
parmetros
al
constructor
de
la
misma.
Pero si adems de un constructor con parmetros queremos seguir
manteniendo el constructor "predeterminado", entonces tendremos que
declararlo aunque no escribamos nada de cdigo en el interior.
En el listado 8 podemos ver la clase Cliente con tres constructores.

Public Class Cliente


Implements IComparable, IFormattable
'
Private _nombre As String
Private _apellidos As String
Private _saldo As Decimal
'
Public Sub New()
End Sub
Public Sub New( _
ByVal elNombre As String, _
ByVal losApellidos As String)
_nombre = elNombre
_apellidos = losApellidos

End Sub
Public Sub New( _
ByVal elNombre As String, _
ByVal losApellidos As String, _
ByVal elSaldo As Decimal)
_nombre = elNombre
_apellidos = losApellidos
_saldo = elSaldo
End Sub

Listado 8

LOS CONSTRUCTORES NO SE HEREDAN


El constructor es el nico miembro de una clase que no se
hereda, por tanto si necesitamos crear constructores en clases
derivadas debemos declararlos de forma explcita.

LOS CONSTRUCTORES DE LAS ESTRUCTURAS


Los constructores de las estructuras son un caso especial, ya que siempre
existir un constructor sin parmetros, el cual adems no podemos definirlo
por medio de cdigo, porque es el propio compilador el que se encargar de su
creacin. Por tanto en las estructuras solamente podemos definir constructores
parametrizados (que reciban parmetros) y en el caso de definirlo, en el cdigo
del mismo, tendremos que asignarle un valor a cualquiera de las propiedades
(o campos) pblicos que tenga esa estructura, siempre y cuando no sean
estticos (compartidos).

CONSTRUCTORES QUE USAN OTROS CONSTRUCTORES


Debido a que los constructores pueden recibir parmetros, en algunas
ocasiones nos puede ser til poder llamar a otros constructores, por ejemplo
de la clase base o de la misma clase.

En estos casos, en Visual Basic es fcil hacerlo, como sabemos que los
constructores realmente son mtodos llamados New, los podemos usar de la
misma forma que haramos con cualquier otro mtodo.
Por ejemplo, si modificamos el cdigo mostrado en el listado 8, podramos
hacer esto:
Public Sub New( _
ByVal elNombre As String, _
ByVal losApellidos As String, _
ByVal elSaldo As Decimal)
Me.New(elNombre, losApellidos)
_saldo = elSaldo
End Sub

De forma que desde el constructor que recibe tres parmetros llamemos al que
recibe dos.
En este caso, la instruccin o palabra clave Me representa a la instancia actual
(el
objeto
que
se
ha
creado
en
memoria).
Si en lugar de llamar a otro constructor de la propia clase, queremos llamar a
un constructor de la clase base, en lugar de Me, usaremos MyBase.
En C# este mismo cdigo se hara de la siguiente forma:
public Cliente(string elNombre,
string losApellidos,
decimal elSaldo)
: this(elNombre, losApellidos)
{
_saldo = elSaldo;
}

Es decir, se llamara al otro constructor indicndolo despus del cierre de


parntesis y separndolo con dos puntos.
En C# la instruccin o palabra clave que hace referencia a la instancia actual es
this y la que hace referencia a la clase base es: base.

CLASES ABSTRACTAS

Tal como comentamos el mes anterior, en ocasiones tendremos la necesidad de


crear clases que no se puedan usar para crear objetos, pero si para usarla
como
base
de
otras.
La razn principal para que hacer que una clase no sea instanciable es que
dicha clase por s sola no tenga ningn sentido, al menos para poder crear
nuevos objetos de ese tipo, pero si tendr sentido si la usamos como clase
base de otras clases.
Por ejemplo, podramos tener una clase Animal, la cual no tendra sentido si a
partir de ella se pudiesen crear nuevos objetos, ya que el concepto de animal
es demasiado abstracto para poder crear un objeto a partir de l. Pero si la
podramos utilizar para derivar de ella otras clases que bien podran ser a la
vez abstractas o bien clases "normales".
.NET Framework nos permite crear clases abstractas, las cuales son como las
interfaces, pero las que pueden tener mtodos y otros miembros que tengan
no solo la definicin de esos miembros sino tambin cdigo funcional. Adems,
debido a que las clases abstractas estn pensadas para usarse como clases
base de otras, todos los miembros son virtuales de forma predeterminada, por
tanto no es necesario indicarlos usando el modificador Overridable (virtual
en C#).

DECLARAR CLASES ABSTRACTAS


La definicin de una clase abstracta se hace como las clases normales, salvo de
que hay que usar el modificador MustInherit (abstract en C#), de esta
forma le indicamos al compilador de que nuestra intencin es la de que nuestra
clase sea "heredable".
Como ya hemos comentado anteriormente, el concepto de clases abstractas o
clases que solo se pueden usar como clases base es importante cuando
queremos ofrecer cierta funcionalidad a nuestras clases, sobre todo las que
formen parte de ciertas jerarquas de clases, ya que las clases abstractas nos
servirn para definir el comportamiento del resto de las clases, adems de que,
como las interfaces, nos permitirn disponer de cierta funcionalidad o, mejor
dicho, nos permitirn dar unas pautas que los que decidan usarlas, tendrn
que
seguir.
Pero a diferencia de las interfaces, las clases abstractas no solo representarn
un contrato, sino que tambin pueden ofrecer "de fbrica" un funcionamiento
que, aunque las clases derivadas no implementen, stas ya tendrn, en este
aspecto funcionarn como las clases normales, ya que las clases derivadas
podrn usar el cdigo implementado en ellas.

MIEMBROS ABSTRACTOS
Cuando comentamos que las clases abstractas son como las interfaces no solo
nos referamos a que se podran usar para proporcionar polimorfismo, sino

porque en las clases abstractas tambin podemos definir miembros que a su


vez sean abstractos, es decir, que en las clases abstractas solamente se defina
el mtodo o propiedad, pero que no tenga ninguna funcionalidad, es ms, si
declaramos un miembro como abstracto la clase que se derive de la clase
abstracta
estar
obligada
a
definirlo.
Esto nos ofrece la ventaja de poder definir miembros funcionales y miembros
que solo tengan sentido en las clases derivadas y por tanto sern las clases
derivadas las que deban definir el cdigo que los haga funcionales y le den la
utilidad adecuada.
Para indicar que un miembro es abstracto, debemos indicarlo usando la
instruccin MustOverride (abstract en C#), de esta forma nos permitir el
compilador escribir solo la definicin del mismo y no tener que escribir ningn
cdigo.
En las clases derivadas tendremos que usar la instruccin Overrides
(override en C#) de la misma forma que lo usamos con el resto de las clases
para indicar que dicho mtodo est reemplazando a un miembro definido en la
clase base.
Una puntualizacin: Los miembros abstractos solo se pueden definir en clases
abstractas.
En el cdigo incluido en el ZIP que acompaa a este artculo se incluye un
ejemplo que define y usa las clases abstractas, como es habitual, se incluye
cdigo para Visual Basic y C#.

CLASES SELLADAS (NO HEREDABLES)


Otro concepto, que posiblemente pueda parecer que no est "ligado" con la
herencia es el de las clases selladas o no heredables.
Este tipo de clases no permitirn que se usen como clases base de otras
nuevas clases.
La existencia de las clases normales y las abstractas nos permiten derivar
nuevas clases a partir de ellas, eso tiene sentido, pero, qu sentido puede
tener una clase de la que no se puedan derivar nuevas clases?
La razn principal para definir una clase como sellada o no heredable es
precisamente porque no queremos que nadie pueda modificar el
comportamiento de dicha clase, ya que al no poder usarla como base de
nuevas clases, nadie podr ofrecer nueva funcionalidad, de esta forma nos
aseguramos que esa clase siempre funcionar como la hemos definido.
Esto es as, porque al estar "sellada" tampoco podremos definir miembros
virtuales, por la sencilla razn de que nadie podr derivar nuevas clases y por
tanto tampoco podr reemplazar el comportamiento de los mismos.

Debido a que el comportamiento normal de una clase es que sea heredable,


para poder crear clases que estn selladas, y por tanto hacerlas no heredables,
debemos usar la instruccin o modificador NotInheritable (sealed en C#).

Conclusiones
Confo que todo el tema tratado sobre la programacin orientada a objetos, y
por extensin a la herencia, desde el punto de vista de un programador de
.NET Framework nos permita afrontar todo este tema, que si bien al principio
puede parecer algo "escabroso", realmente debera ser una forma natural de
programar, sobre todo si tenemos en cuenta que todo el .NET Framework se
basa en las clases, interfaces y dems conceptos relacionados con la POO.

Nos vemos.
Guillermo

El cdigo de ejemplo (para Visual Basic y C#): POO_Iberprensa.zip 19.80 KB


MD5 checksum: 9E8FB3E10DD7CCC8826D2B0C1D5F6533

Nota:
Los proyectos estn creados con Visual Studio .NET 2003 pero
se pueden usar en cualquier versin de Visual Studio 2005,
incluso las versiones Express, aunque en este ltimo caso,
tendrs que cargar de forma independiente los proyectos de
Visual Basic o de Visual C#.

También podría gustarte