PROGRAMACION ORIENTADA A OBJETOS (OOP) DESDE CERO
INSTRUCCIONES ANTES DE EMPEZAR
Si te interesan mis opiniones personales, alguno que otro chiste y todas esas tonterías que nos encanta colocar cuando sabemos que alguien va a leernos, entonces parte con la INTRODUCCION
Si quieres entender el asunto de los objetos y que significa toda esa jerga ampulosa ,salta directo a EMPECEMOS CON LA TEORIA
Y si eres un programador de Visual Basic que solo le interesa saber que diablos son los famosos módulos de clase, no le hagas caso a ninguna de estas dos secciones y lee LA PRACTICA: CLASES EN VISUAL BASIC,
CREDITOS
¿Y creen que todo esto lo aprendí de nacimiento? noooo pues, empecé ayer en la noche, desde la más completa ignorancia, buscando y buscando entre los links de mi página de recursos hasta que encontré los excelentes cursos del instituto español http://www.eidos.es/ allí me matriculé en un curso gratuito de introducción a la OOP, que resultó ser tan bueno, que incluso un tonto como yo lo está entendiendo. La parte práctica está inspirada en un extraordinario artículo de la página http://come.to/vbasic corto, preciso e ilustrativo, tal como a mi me gustan (felicitaciones)
INTRODUCCION
Estas notas están dirigidas a programadores antiguos, los que crecimos en la època de los lenguajes procedurales, para quienes programación moderna era sinónimo de "estructurada" y que no entendemos maldita cosa acerca de la encapsulación, polimorfismo, clases y toda esa hype propagandistica con que les lavan el cerebro a la juventud actual (en mis tiempos hijitos queridos...ñaca ñaca).
No hay que engañarse: la OOP, es lo menos intuitivo que he conocido, a pesar de que le echen la culpa a la 'formación antigua' no es eso. Yo aprendí los fundamentos del Fortran en unos pocos minutos, para que hablar del Basic, creo que nací sabiendolo. Para entender el asunto de los objetos pasé un montón de tiempo buscando alguna fuente que me aclarara las cosas de manera rápida y sencilla.
Esto es desanimante para los que obtenemos algun placer al conseguir un código elegante o particularmente ingenioso. Como la mayoría de las cosas complicadas, la OOP es extremadamente sosa en su esencia., y presumo que una vez dominado el lenguaje y la operatoria, programar según los cánones de la OOP debe ser tan fácil como transcribir un libro a máquina, e igual de aburrido.
Nótese que al hablar de OOP no nos referimos a ningún lenguaje en particular, pues se trata de un método y no de un lenguaje específico (tal como lo fué la programación estructurada en su época). Los lenguajes C++ y Java son los mas conspicuos entre los diseñados específicamente para cumplir los requisitos de la OOP, Visual Basic a partir de la versión 7.0 traerá las cuatro funcionalidades fundamentales de la OOP: Herencia, encapsulación, polimorfismo y sobrecarga
Pero también tiene sus ventajas, en realidad LA ventaja a mi modo de ver es la facilidad para heredar y reutilizar código. Esto no es nada nuevo y todo programador más o menos antiguo sabe que sin el cut-and-paste sería hombre muerto, la diferencia es que la OOP promete, al menos en teoría un método racional de reutilizar el código ya escrito para otros propósitos. En éste campo los métodos de la OOP si tienen sentido y utilidad para cualquiera. Es muy dudoso que sín este tipo de programación se hubiese conseguido hacer funcionar mamuts como el Windows y su entorno.
Como dije antes estas son fresquísimas impresiones de alguien que recién ayer estaba en la mas absoluta ignorancia sobre el tema. Seguramente cometeré muchas equivocaciones y me tendré que morder la lengua en mis opiniones cuando sepa un poco más, pero aprender junto con un ignorante también tiene sus ventajas, especialmente porque doy muy pocas cosas por sabido. En fin, basta de generalidades y:
EMPECEMOS CON LA TEORIA
¿Qué es un objeto?, un objeto es una cosa. Cualquier cosa y me imagino que se usa el concepto para dar la idea que los métodos de la OOP son aplicables sobre cualquier cosa en el ámbito de la programación (los origenes de estos conceptos medio absurdos están en la teoría de conjuntos, algo muy querido por los profesores de estructura de datos y cosas así).
Un objeto se caracteriza por ser algo muy general, algo tan general que puede ser cualquier cosa. En la OOP los objetos tienen
Datos (o características) y
Métodos (o comportamiento)
y una instancia es un caso particular de un objeto general, un objeto puede ser una instancia de una clase. Por ejemplo el objeto Mi_Edad puede tener la instancia 45 y puede pertenecer a la clase de los números enteros, dicho en código:
int Mi_Edad:=45
Si estoy programando en Java por ejemplo int es una clase que viene incluida en el lenguaje. Pero yo también podría crear mis propias clases. Por ejemplo puedo crear una clase de marcos que contenga las cajas de mensaje para mis programas, en código algo más o menos así
Marco CajaMensaje:= new Marco()
Las clases int y marco son conceptualmente lo mismo, solo que una viene dada por el lenguaje y la otra la definimos nosotros mismos (si, así es como podemos enriquecer un lenguaje con nuestras propias clases si eso es lo que están pensando)
Así tenemos que una CLASE un tipo particular de objetos, es una coleccion de objetos con sus datos y métodos correspondientes. Si todo esto les huele a teoría de conjuntos, no se extrañen, es mas o menos el mismo cuento de los conjuntos, subconjuntos y conjuntos de conjuntos. Solo cambian los nombres pero la idea es esa
Hablando computacionalmente un objeto podemos pensarlo como un array de varias dimensiones, con datos y étodos. Los métodos son punteros que apuntan a funciones que trabajan con los datos. Enredado ¿no? que más da, para lo que sirve...
LA HERENCIA
Ejemplo: supongamos que yo programo una clase que lee números enteros ingresados por teclado, le calcula el IVA a cada número y lo graba. Ahora bien, supongamos que ahora deseo tener otro procedimiento que pueda hacer lo mismo con números reales.. En la programación tradicional uso el cut-and-paste y cambio lo que sea necesario, pero con OOP solo creo una nueva subclase, que heredará todos los métodos de la clase padre y solo necesito redefinir el método de lectura del teclado.
Y así nos vamos, creando nuevas clases heredando las antiguas y modificándolas según nos convenga. Las cualidades comunes a las distintas clases forman una superclase. Por ejemplo puedo hacer una superclase INVENTARIO de la cual derivo las clases ventas, compras, ingresos nuevos, etc. La clase padre contiene los métodos comunes a las demás y solo hay que añadir los métodos que se necesiten al crear una nueva clase. Si una clase padre contiene el método IMPRIMIR por ejemplo, éste estará disponible para todas sus hijas
La herencia permite reutilizar todo el código escrito para las superclases, escribiendo solo las diferencias específicas que necesita la subclase. Al heredar, la subclase toma directamente todos los métodos de la superclase e indirectamente todos los de las clases superiores a ella en su rama
Se heredan los datos (características del objeto) y los métodos (el comportamiento). Usualmente son los métodos los que se modifican
Hay clases abstractas o virtuales que no hacen nada (no tienen métodos) y solo sirven para basar otras subclases, algo así como agruparlas bajo un mismo tipo
La subclase solo hereda miembros visibles desde su clase hija, y tiene forzozamente que redefinir alguno de los métodos de la clase padre (si no sería la misma)
Un método se puede sustituir completamente o solo modificar: al sustituir se conserva el mismo nombre y la clase hija queda con un método de igual nombre pero que se comporta de distinta manera que el de su padre.
Si en cambio solo modificamos un método heredado (es decir lo ampliamos), necesitará usar primero el método del padre (herencia) y después el del hijo. Como ambos se llaman igual hay que diferenciarlos usando el prefijo this (o self) para la clase hija y super para la clase padre
LA ENCAPSULACION
Un ejemplo práctico. Supongamos que defino la clase recuadro, para dibujar un cuadro an la pantalla, con los datos y métodos que se muestran a continuación:
DATOS |
METODOS |
| (x,y) sup. izquierda | mostrar |
| (x,y) inferior derecha | ocultar |
| linea | mover |
| color de linea | |
| color de relleno |
Para dos recuadros podríamos tener los siguientes valores (por ejemplo)
DATOS |
valores objeto 1 | valores objeto 2 |
| (x,y) sup. izquierda | 7,5 |
14,50 |
(x,y) inferior derecha |
22,12 |
20,60 |
linea |
doble |
simple |
color de linea |
verde |
verde |
color de relleno |
azul |
rojo |
Note como los datos (o propiedades) son los mismos para todos los objetos de la clase, sus valores son distintos, asi tenemos un cuadro azul en la punta superior izquierda de la pantalla y otro rojo en la punta inferior derecha
Los métodos harán lo obvio según el nombre:
- mostrar :muestra el cuadro
- ocultar oculta el cuadro
- mover mueve el cuadro a una nueva posición
Un método de una clase puede llamar a otros de su misma clase y cambiar sus valores. Todos los datos de una clase son privados y deben accederse mediante métodos públicos.
Privados, significa que solo pueden intercambiar informacion dentro de su mismo nivel, públicos pueden recibir valores desde otros niveles.
Por ejemplo para modificar la coordenada y1 (de (x,y) superior izquierda) podríamos hacerlo de dos maneras:
- asignandole directamente un valor desde afuera (por ej. RecuadroGeneral y1=12)
- invocándo un método, por ejemplo 'poner' RecuadroGeneral.Poner y1=12
El segundo método es el que se debe usar para mantener el principio de la encapsulación, que dice que una clase debe ser una estructura cerrada, solo se debe acceder a ella a través de los métodos definidos para ella y nada más
Si definimos la variable y1 del tipo publico (es decir que puede asignarsele valores desde cualquier punto del procedimiento, estaremos violando el principio de la Encapsulación
Para evitar estas asignaciones directas y crear clases 'encapsuladas' algunos datos se pueden definir de solo lectura o bien declararlos como variables privadas. Existe sin embargo una excepción a esta regla: se pueden hacer públicos los datos que la clase no utiliza ¿y para que tener datos que no utiliza?... bueno, dejemoslo hasta ahi porque ni a mi se me ocurre para que podría servir tal cosa. Dicen que para algo sirve.
Para que un método pueda actuar hay que mandarle un mensaje a la clase. Las clases son entonces como 'cajas negras' que ejecuitan sus métodos cuando reciben un mensaje, manteniendo su operación interna cerrada e inaccesible desde el exterior.
POLIMORFISMO
El polimorfismo es la cualidad que tienen los objetos para responder de manera distinta ante el mismo mensaje. Por ejemplo si enviamos el mensaje IMPRIMIR a distintos objetos, cada uno hará algo distinto según lo que tengan definido como imprimir, así uno imprimirá a espacio simple otro a doble espacio, otro con distinto tipo de letra, etc.
ºEl polimorfismo es consecuencia natural de que podemos sustituir los métodos, o sea dos objetos pueden tener los metodos de igual nombre pero actuarán de manera distinta según hayan sido definidos dentro de su clase.
La gran ventaja del polimorfismo es que n métodos con igual nombre cercanos a la raíz del arbol de clases, afectan a todas las clases hacia abajo.
SOBRECARGA
La sobrecarga consiste en que varios métodos pueden tener el mismo nombre, siempre y cuando el tipo o número de parametros que reciban sea diferente, por ejemplo, para la clase FILE definimos el método WRITE
FILE::WRITE(int i);
FILE::WRITE(long l);
FILE::WRITE(STRING EESO);
etc..
(continuará...algún día...)
LA PRACTICA: CLASES EN VISUAL BASIC
El caso es que lo que a mi me interesa es como afecta todo esto a mi trabajo de programación en Visual Basic, específicamente si puede o no ayudarme en algo para hacer mejor código o para ahorrarme algún trabajo. Busqué y busqué y no encontraba ningún ejemplo práctico, sencillo que me alumbrara el camino. Todos los ejemplos que encontré eran alambicados, inútiles o dificiles de seguir, finalmente encontré en.http://come.to/vbasic el conciso e ilustrativo artículo quepuedes ver haciendo click en el botón POO de esa página.
La verdad es que yo solo leí el primer ejemplo (el más simple, de la ventana), lo reproduje en mi computador y, modificando aquí y allá, en una tarde de prueba y error conseguí hacer mi primera aplicación práctica programada en un módulo de clases.
El ejemplo de la ventana
Se trata de abrir un proyecto con un form al que se le agrega un módulo de clase, en el módulo de clase declaramos como Publica la variable estado y escribimos dos funciones privadas: abrir y cerrar
ventana.cls (módulo de clase) Public estado as integer '1 abierta, 0 cerrada Public Sub abrir()
...........estado = 1
End Sub
Public Sub cerrar()
...........estado = 0
End Sub
Luego en el Form_Load del formulario del mismo proyecto (Form1) escribimos lo siguiente:
FormVentana.frm (formulario) Dim Ventana_salita As New ventana ' Creamos mi Ventana_salita
Ventana_salita.abrir 'Abramos la ventana de la salita
estado = Ventana_salita.estado 'comprobemos que efectivamente se ha abierto
Ventana_salita.cerrar 'Cerremos ahora la ventana
estado = Ventana_salita.estado
Y tenemos listo nuestro primer programa con módulos de clase, que no hace absolutamente nada, aparte de cambiar el valor de la variable estado. En realidad el programa es tan primitivo que para ver el efecto hay que poner breackpoints en cada línea del Form_Load y ver los valores de variable con el debug. Mi primera modicficación fué agregarle dos botones a la form (abrir y cerrar) y unas msgbox que mostraban el valor que iba tomando la variable 'estado'. En fin, el asunto no daba para más a si es que me puse manos a la obra a programar una clase para grabar y recuperar datos en archivos ascii planos.
Manejo de archivos random planos con módulos de clase
Primero que nada hice la interfase, y esta vez me tomé la molestia de colocarle nombres significativos a los objetos (en lugar de usar los nombres por defecto) y de usar la Option Explicit, que te obliga a declarar todas las variables (hay que abandonar las malas costumbres, aunque sea de a poco y nos cueste sangre). La cosa quedó más o menos así:

Bueno, creo que la funcionalidad del programa se desprende mas o menos clara de la figura, así que lo primero fué insertar un módulo de clase que le puse 'ventana' aunque el nombre no tiene nada que ver con la funcionalidad lo hice por pura flojera, adivinen...(las malas costuimbres)
Enseguida pasé a programar los métodos leer, grabar, agregar, listar (que corresponden a los botones de mi form) y que no son más que funciones privadas
El módulo de clase. paso a paso (o casi)
Al principio de la clase declaré las variables nombre, direccion y telefono como strings publicos (para leer la información dede el form)
También definí las variables pos (la posición en que se graba un registro) y puntero (la posición del último registro, para agregar) como enteros
Luego definí los campos Xnombre, Xdireccion y Xtelefono del archivo plano para esto se usan los tipos definidos por usuario con type... end type y luego un private dat as registro que le asigna esos campos como 'apellidos' de la variable dat
Enseguida vienen los métodos abrir(archivo), cerrar e incrementa_puntero. Estos tres métodos son solo de uso interno, dentro de la misma clase como veremos más adelante.
Los dos primeros se explican por si mismos, incrementa_puntero merece alguna explicación. En este ejemplo reservamos el primer registro del archivo para guardar el puntero, que es un número que se incrementa en uno cada vez que agregamos un registro, también nos sirve para saber en que posición va la cola del archivo para agregar los registros nuevos. Lo primero que hace este método es llamar a otro método dentro de la clase que se llama lee_puntero, el cual a su vez llama al método abrir(archivo), lee el primer registro y almacena el valor de dat.Xnombre en la variable puntero, luego cierra el archivo.
Retornando el control a incrementa_puntero se vuelve a invocar abrir(archivo) y si el puntero tiene el valor cero, se cambia a uno, para asegurarse que el primer registro quedará en la pocisión 2 y no sobreescribirá el lugar donde se guarda el puntero. Luego se incrementa el puntero en uno y se vuelve a grabar en la primera pocisión del archivo usando el campo Xnombre
Creo que si has entendido hasta ahora las demás rutinas ya no necesitan explicación
ventana.cls (módulo de clase)
Public nombre, direccion, telefono As String
Public pos, puntero As Integer
Private Type registro
...........Xnombre As String * 30
...........Xdireccion As String * 30
...........Xtelefono As String * 20
End Type
Private dat As registro
Public Sub abrir(archivo)
...........cerrar
...........Open archivo For Random As 1
End Sub
Public Sub cerrar()
...........Close
End Sub
Public Sub lee_puntero()
...........abrir (archivo)
...........Get 1, 1, dat
...........puntero = Val(dat.Xnombre)
...........cerrar
End Sub
Public Sub incrementa_puntero()
...........lee_puntero
...........abrir archivo
...........If puntero = 0 Then puntero = 1
...........puntero = puntero + 1
...........dat.Xnombre = puntero
...........Put 1, 1, dat
...........cerrar
End Sub
Public Sub leer(archivo, pos)
...........abrir (archivo)
...........Get 1, pos, dat
...........nombre = dat.Xnombre
...........direccion = dat.Xdireccion
...........telefono = dat.Xtelefono
...........cerrar
End Sub
Public Sub grabar(archivo, pos)
...........abrir (archivo)
...........dat.Xnombre = nombre
...........dat.Xdireccion = direccion
...........dat.Xtelefono = telefono
...........Put 1, pos, dat
...........cerrar
End Sub
Public Sub agregar(archivo)
...........incrementa_puntero
...........abrir (archivo)
...........dat.Xnombre = nombre
...........dat.Xdireccion = direccion
...........dat.Xtelefono = telefono
...........Put 1, puntero, dat
...........cerrar
End Sub
El código del form
Programada la clase pasamos a programar nuestra form, Aquí no voy a entrar en explicaciones porque creo que basta con mirar el código, reproducirlo y analizarlo queda así:
| FormArchivo.frm |
| Private Sub agregar_Click() ............Ventana_mia.nombre = BoxNombre.Text ............Ventana_mia.direccion = BoxDireccion.Text ............Ventana_mia.telefono = BoxTelefono.Text ............Ventana_mia.agregar archivo ............BoxPosicion.Text = Ventana_mia.pos End Sub Private Sub grabar_Click() Private Sub leer_Click() Private Sub Boxlistar_Click() |
Y se me olvidaba, el módulo de declaraciones globales
Para no tener que estar repitiendo en todos lados la declaración de la clase y el nombre del archivo, los coloqué en un módulo así
ModGlobales.bas Option Explicit
Public Ventana_mia As New ventana
Public Const archivo = "prueba"
Un detalle final
¿Y alguno de ustedes se creyó el cuento de que partí ordenadamente programando las funciones de los módulos de clase, luego las form y todo eso? NAAAA ¿por que creen que el modulo de clase se llama ventana? ¡Bingo! porque tomé el mismo ejemplo original de la ventanita y lo fuí modificando hasta que, después de mucha sangre, trabajos, sudor y lágrimas llegué al aparentemente ordenado producto que les muestro. En el camino... el caos
MIS CONCLUSIONES
Ya se que es arriesgado dar conclusiones sobre algo que no se domina, pero ¿que más da? aunque me equivoque estas son mis primeras impresiones sobre este asunto:
Nada muy nuevo en realidad. La OOP es, en gran medida un esfuerzo linguistico, el mismo viejo código con nuevas normas (bastante buenas a decir verdad): las clases son herederas de los tipos definidos de usuario (esos del Type....End Type que colocaban 'apellido' a las variables), de las funciones y aún de las subrutinas de nuestro querido y viejo GWBasic. Hace unos 10 años atrás me di cuenta que era importante hacerme de un stock de subrutinas y funciones genéricas para echar mano cambiándole unas cuantas cosas. La OOP es la formalización de ese concepto, que es más viejo que sentarse sobre las nalgas.
La única diferencia real que veo en todo esto es que todas las variables que se usan al interior de una clase son privadas, lo que las hace actuar como una caja negra de código. También el asunto ese de la herencia, me parece muy bien. ¿Por que digo esto?, bueno en el excelente curso de Eidos, mi profesor dice:
"No piense de forma procedural: En OOP no se puede pensar de una forma procedural, una clase no es un conjunto de funciones relacionadas, es un objeto tan real como los demás que están fuera del ordenador. Los objetos son de verdad, no son cajas de almacenamiento de código"
Lo siento profe, muy bueno el curso pero los objetos sis son, a mi modo de ver, cajas de almacenamiento de código ¿un objeto es algo real? ni en sueños..
Para los que hemos estado expuestos al Visual Basic tenemos bastante avanzado en la terminología de los objetos, que es, a mi modo de ver la principal dificultad del asunto. En Visual Basic tenemos controles con propiedades, métodos y eventos. Un CommandButton por ejemplo tiene la propiedad Caption (Command1.caption="Ok"), el método Setfocus (Command1.Setfocus) y el evento click (Private sub Command1_Click... etc..) .por lo que ya tenemos infiltrados intuitivamente algunos conocimientos importantes. Sumando y restando, los conceptos de la OOP me parecen muy bien especialmente aplicados sobre grandes proyectos. En proyectos pequeños y aplicaciones del lado cliente (que es lo que yo hago) está Ok, siempre que se tome como una guía y no como una biblia inflexible