Programación Orientada a Objetos (I)

Iniciamos este artículo con un poco de teoría, a veces algo ardua pero imprescindible. Vamos a dedicarlo a un concepto de vital importancia en el desarrollo de aplicaciones hoy día: el paradigma de la Programación Orientada a Objetos (POO, u OOP por sus siglas en inglés).

Soy consciente de que la mayoría de los lectores estáis, en mayor o menor grado, familiarizados con este modo de trabajo. Si lo deseáis, podéis omitir la lectura de este texto. Sin embargo, yo no podía omitir su redacción, por aquellos de vosotros que, aunque hayáis oído mencionar el concepto, no hayáis, aún trabajado de esta manera.

Si hace ya tiempo hablar de Objects Oriented Programming era casi un esnobismo propio de una élite que trabajaba en grandes proyectos, actualmente es inconcebible pensar en ningún desarrollo serio que no siga esta pauta. Por esta razón, me he decidido a sentar aquí algunas bases fundamentales.

LA PROGRAMACIÓN ORIENTADA A OBJETOS

El fundamento de este modo de trabajo se basa en tratar los procesos necesarios para resolver un problema como si fueran objetos del mundo real. Un desarrollo (sea un aplicativo web, una aplicación de escritorio, o lo que sea), está formado por artefactos que resuelven problemas o cubre necesidades de los usuarios. En lugar de crear esos artefactos de un modo secuencial y desorganizado los creamos y manipulamos como si de objetos reales se tratase.

Y, en la práctica ¿qué significa esto? Bien. Pensemos en un objeto cotidiano, que todos tenemos en casa: un televisor. Un televisor tiene una serie de características: color de la carcasa, tamaño, tipo de pantalla, peso… Y también implementa una serie de funcionalidades: encenderse o apagarse, subir o bajar el volumen, cambiar de canal… Traducido al mundillo de la programación, las características se llaman propiedades o atributos, y las funcionalidades se llaman métodos. Familiarízate con estos términos, porque serán, desde ahora y para siempre, el pan de cada día.

Cuando «pones la tele» en casa, tu no sabes (ni te importa) lo que ocurre dentro del mando, ni dentro del televisor cuando pulsas el botón. Simplemente, te limitas a pulsarlo y aquello se enciende. Los circuitos encargados de hacer que eso ocurra, o la forma en que trabajan, ni te incumbe, ni quieres saberlo. Con un objeto de programación ocurre lo mismo. Tu sabes que lo usas, llamas a tal o cual método del mismo, y hace lo que esperas que haga. Como lo hace «por dentro», no te preocupa, ni tiene por qué (a menos, claro, que hayas participado en la creación del objeto como autor del mismo, al igual que, si eres técnico en electrónica, puede que hayas hecho reparaciones o mejoras en tu televisor, y sí sabes como funciona).

Este modo de trabajar tiene muchas ventajas. Cuando tienes un objeto que resuelve un determinado problema concreto (digamos, por ejemplo, leer los registros de una base de datos), ya no necesitas pensar en cómo resolver ese problema en cada nuevo desarrollo que haces. Simplemente, implementas ese objeto, y él lo hace por ti. Esto se llama reutilización de código, y te ahorra muchas horas de trabajo, y muchos quebraderos de cabeza.

Además, enseguida nos damos cuenta de otra ventaja importante. Si eres un poco organizado, en tu código no necesitas poner un objeto que lea los registros de una base de datos en cada sitio donde tengas que leerlos. Lo pones en un solo sitio en todo tu proyecto, y lo invocas desde donde te haga falta. Esto es lo que se conoce como desacoplamiento. Así, si mañana tu objeto debe ser actualizado, o mejorado de algún modo, sólo tienes que tocar en un sitio. Te facilita enormemente el mantenimiento y escalabilidad de tu trabajo.

La facilidad de lectura y documentación es otro de los factores relevantes a la hora de usar esta metodología. Para tí es mucho más fácil documentar tu trabajo si sólo tienes que hacerlo en un sitio. Y para otro desarrollador que participe contigo en el proyecto es mucho más fácil entender lo que has hecho, y cómo lo has hecho.

Aunque te he mencionado las principales ventajas de este modo de trabajo, hay otras muchas que iremos descubriendo.

CLASES

Aquí aparece otro concepto imprescindible: las clases. ¿Has ido a algún centro comercial y visto los televisores en exposición en la zona de electrónica? Son todos parecidos ¿verdad? Y los de una misma marca y modelo son todos iguales, así que, cuando eliges el modelo que quieres comprar, no te preocupas de más. Coges el que está más a mano y punto. Las carcasas de los televisores se hacen con moldes de inyección de plástico. Las pantallas también se hacen con moldes. Los circuitos internos se cortan con moldes… En programación ocurre lo mismo. Los moldes se llaman clases. Una clase tiene definidas las propiedades y métodos necesarios. El objeto se crea a partir de una clase, de modo que también tiene, desde el momento de su creación, las propiedades y métodos que necesita. Técnicamente, crear un objeto se llama instanciar una clase. Por lo tanto, decimos que un objeto es la instancia de una clase. La programación de lo que hará el objeto, y cómo lo hará no se escribe en el propio objeto. Se escribe en la clase. Cuando hay que mejorar o ampliar un objeto, no se hace en el objeto, sino en la clase.

HERENCIA

Sigamos pensando en televisores. El fabricante de un modelo ha estado usando el molde de la carcasa para fabricar sis televisores. De repente, un día se da cuenta de que los televisores mejorarían, y darían menos averías si la carcasa tuviera otro agujero de ventilación (por ejemplo). ¿Que hace entonces? ¿Diseña un nuevo molde desde cero? No. El molde que tiene le sirve, con la sola excepción de añadirle una pieza que haga el nuevo agujero de ventilación. Prácticamente, copia el molde anterior, con una pequeña modificación.

Y ¿cómo se traduce eso en programación? Bien. Tu tienes una clase que hace tal o cual cosa, y la hace bien. Pero tienes que añadirle una nueva funcionalidad (un nuevo método). Lo que haces es crear una nueva clase, que tenga todos los métodos y propiedades de otra que ya tienes, e implementas el nuevo método. Tu no repites los métodos y propiedades que ya tenías. Simplemente dices que la clase que llamamos, digamos, ClaseNueva hereda de la que ya tenias (digamos que se llama ClaseVieja). Por el mero hecho de decirle que una clase hereda de otra, ya tiene todos los métodos y propiedades que tiene aquella de la que hereda. No es necesario reimplementarlos. Es una copa de la clase original, sin tener que copiar su código interno. Sólo debes añadir el método nuevo.

A la clase original se la llama clase padre. A las clases que derivan de esta se las llama clases hijas, o clases derivadas. Al proceso de crear una clase a partir de otra se le conoce como heredar de una clase o extender una clase.

Si instancias un objeto de una clase derivada, tendrá todos los métodos y propiedades de la clase padre, más los que hayas implementado en la clase derivada. Si instancias un objeto de la clase padre, tendrá todos los métodos y propiedades de la clase padre, pero no los de la clase derivada. Es decir, la herencia funciona de arriba a abajo, pero no al revés. Supongamos que tu padre tiene un chalet. Llegado el momento, tú heredas el chalet. Si ahora te compras, también, un apartamento, tu tienes los dos inmuebles. El que has heredado de tu padre, y el que has adquirido tú. Esto es parecido.

HERENCIA MÚLTIPLE

Algunos lenguajes de programación soportan lo que se conoce como herencia múltiple. Esto significa que una clase puede heredar de varias al mismo tiempo, heredando, inherentemente, todas las propiedades y métodos de todas las clases de las que hereda. Algunos vemos esto como un problema. Suponte que creas una clase que hereda de dos clases padre. Y ahora suponte que cada una de las clases padre tiene un método que se llama igual en ambas, aunque hace cosas diferentes, o las hace de diferente modo. Ahí podría aparecer una colisión de nombres. Algunos lenguajes, como Python, solucionan esto de una forma tajante. En la clase derivada se toma el método de la primera clase que se cita al crear la herencia. El método de la segunda clase, simplemente, se ignora. Otros lenguajes, como Java o PHP, han optado por una solución, a mi juicio, mejor: simplemente no admiten la herencia múltiple.

CONCLUYENDO

Aunque este artículo ha sido una introducción muy breve, y aún nos quedan muchos conceptos que ver, como base meramente teórica nos vale para empezar. En el próximo artículo desarrollaremos, con ejemplos prácticos, lo que aquí hemos comentado y, en otros artículos, veremos, también con ejemplos prácticos, otros conceptos que no es fácil transmitir de una forma meramente teórica, como herencia sucesiva, interfaces, sobrescritura, sobrecarga, encapsulación, etc. Nos vemos en el próximo artículo.

   

Deja un comentario