Cuando comencé a explorar la programación orientada a objetos (POO u OOP, en inglés), allá por el año 1994, me ocurrió lo que a muchos. Luego de casi 10 años de programar proceduralmente (en Basic, Pascal y Cobol) me encontraba con una nueva visión. Si bien creía entender los conceptos de clase, objeto, herencia y polimorfismo, no lograba ver claramente la diferencia de enfoque a la hora de diseñar un programa. No podía apreciar realmente cuáles eran las diferencias y las similitudes entre el enfoque procedural y el orientado a objetos.
A través de mi experiencia docente varias veces me vi frente a la tarea de introducir los conceptos básicos de la POO, encontrándome con la misma situación, pero desde un lugar diferente: ¿Cómo explicar el nuevo enfoque a personas acostumbradas a programar de forma procedural? ¿Cómo resaltar las similitudes y las diferencias? Fue gracias a un ejemplo del libro «C++ Annotations» que encontré una forma simple de presentar la idea básica detrás de la programación orientada a objetos.
Una consideración más que necesaria: este artículo no se trata de un análisis profundo de la programación orientada a objetos, sino simplemente de una simple y breve introducción, intentando dar un panorama inicial a aquellos que no han tenido contacto con (o nunca han entendido) la POO.
Aclaraciones previas
Antes de desarrollar el ejemplo, aclararemos algunos conceptos que muchas veces se prestan a confusión:
- Lenguajes imperativos: Son aquellos basados en sentencias, ya sean procedurales, orientados a objetos puros o mixtos. Entre ellos se cuentan Pascal, C, C++, Java, Fortran, Perl y Python.
- Lenguajes procedurales: Son lenguajes imperativos basados en procedimientos (o rutinas) y funciones. Entre ellos podemos nombrar a C, Fortran, Pascal (estándar) y Basic.
- Lenguajes orientados a objetos: Son lenguajes imperativos basados en clases (algunos, llamados mixtos soportan también el modelo procedural). Entre los lenguajes orientados a objetos puros podemos nombrar a Smalltalk, Eiffel y Java. Entre los mixtos se encuentran C++ y Python.
- Lenguajes funcionales: Son aquellos basados en funciones matemáticas (y no en comandos o sentencias). Podemos nombrar aquí a ML, Haskell y Lisp.
Un programa procedural simple
Vamos a plantear un ejemplo sencillo. Para ello supondremos un lenguaje imperativo con una sintaxis similar a la de Pascal (usaremos este lenguaje ficticio para simplificar la explicación, en los apéndices al final del artículo podrá encontrar ejemplos equivalentes en PHP, Python y Eiffel).
Supongamos que tenemos el siguiente programa:
program personas;type persona = record
nombre: string;
apellido: string;
edad: integer
end;procedure inicializar(n, a: string; e: integer; var p: persona);
begin
p.nombre := n;
p.apellido := a;
p.edad := e
end;function es_mayor(p: persona): boolean;
begin
return p.edad >= 18
end;function nombre_completo(p: persona): string;
begin
return p.nombre + " " + p.apellido;
end;var
p: persona;begin
inicializar("Juan", "Perez", 25, p);
write(nombre_completo(p));
if (es_mayor(p)) then
writeln (" es mayor de edad.")
else
writeln (" es menor de edad.")
end.
El programa es bastante sencillo. Primero declaramos un tipo persona que es un registro que contiene los campos nombre, apellido y edad. Luego definimos el procedimiento inicializar que toma el nombre, el apellido, la edad y la persona y asigna los primeros a los campos correspondientes de la última. Luego, un par de funciones (es_mayor y nombre_completo) toman una persona y realizan cálculos sobre los valores de sus campos.
En los programas procedurales hacemos esto todo el tiempo: definimos estructuras y tipos de datos y luego creamos procedimientos y funciones que toman como parámetros variables de estos tipos y realizan distintas operaciones sobre ellos. Dicho de otra manera: podemos ver a los programas procedurales como un conjunto de procedimientos y funciones que manipulan estructuras de datos pasadas como parámetros.
Una visión diferente
Podríamos intentar una visión distinta de la situación. Supongamos que nuestro lenguaje nos permite definir acciones y funciones dentro de un registro. Supongamos además que dichas funciones pueden acceder a todos los campos del registro. De esta forma, por ejemplo, el procedimiento inicializar no necesitaría del parámetro p (la persona a inicializar), sino que actuaría sobre la persona a la que pertenece (ya que se encuentra definido dentro del registro.
De esta forma, podríamos reescribir el programa como sigue:
program personas;type persona = record
nombre: string;
apellido: string;
edad: integer;procedure inicializar(n, a: string; e: integer);
begin
nombre := n;
apellido := a;
edad := e
end;function es_mayor: boolean;
begin
return edad >= 18
end;function nombre_completo: string;
begin
return nombre + " " + apellido;
endend;
var
p: persona;begin
p.inicializar("Juan", "Perez", 25);
write(p.nombre_completo);
if (p.es_mayor) then
writeln (" es mayor de edad.")
else
writeln (" es menor de edad.")
end.
Esta visión alternativa tiene varias ventajas. Inmediatamente notamos que los perfiles de los procedimientos y funciones se simplifican, debido a la eliminación del parámetro que representa a la persona sobre/con la cual se realizarán las operaciones.
Una diferencia importante de notación a la hora de usar la estructura de datos definida, es que ya no utilizamos expresiones de la forma funcion(variable), sino que ahora escribimos variable.funcion.
Una ventaja adicional es que la notación es más consistente. Al referirnos a p.nombre_completo no podemos saber (porque en realidad no interesa) si nombre_completo es un campo del registro o una función. Esto es realmente importante: en el caso del campo edad este podría ser reemplazado por una función que calcule la edad de la persona, añadiendo un campo que represente la fecha de nacimiento. De ocurrir esto, los programas que usen el tipo persona no requerirían mayores modificaciones, ya que podrían seguir haciendo referencia a p.edad. Esto facilita la independencia de la implementación y el encapsulamiento, uno de los conceptos claves de la POO.
Ajustando nuestro vocabulario
A lo que antes llamábamos tipo, refiriéndonos a estructuras de datos, ahora lo llamamos clase, entendiendo como tal no sólo las estructuras, sino también el comportamiento asociado (las acciones y funciones asociadas directamente con la estructura de datos).
A lo que antes llamábamos variable ahora lo llamamos objeto. Así como las variables son de determinado tipo, los objetos son de determinada clase.
Los procedimientos y funciones definidos dentro de una clase se llaman métodos, en tanto que los campos se denominan atributos. En nuestro ejemplo, podríamos decir que la clase persona tiene los atributos nombre, apellido y edad, y los métodos inicializar, nombre_completo y es_mayor.
Un concepto importante: la herencia
En la POO existe la posibilidad de extender el comportamiento de una clase, añadiendo atributos y métodos. El mecanismo utilizado para tal fin se denomina herencia.
Siguiendo con nuestro ejemplo, podríamos querer definir una clase empleado. Básicamente un empleado posee todas las y características de una persona (tiene nombre y apellido, tiene sentido preguntarse si es mayor de edad, etc.). Sin embargo, un empleado tendrá otros atributos (por ejemplo, un salario, un cargo) y también otros métodos (liquidar_salario, etc.). Es por esto que bastará con definir a la clase empleado heredando de la clase persona (en POO se usa la expresión «un empleado es una persona«), y añadiendo el nuevo comportamiento (métodos y atributos).
El secreto: un cambio de visión
La POO no es, como muchos afirman, un paradigma distinto del de la programación procedural. Ambas son dos técnicas distintas para abordar la programación imperativa.
El secreto fundamental consiste en dejar de ver a un programa como un conjunto de acciones y funciones que modifican parámetros, para verlo como un conjunto de objetos con comportamiento, y a estos como estructuras que contienen acciones y funciones.
Si bien la POO involucra una serie de conceptos que escapan al alcance de este texto introductorio, el cambio de visión no es mucho más profundo que el pequeño truco que hemos realizado al incorporar a las funciones dentro de una estructura.
Recomendaciones finales
La POO representa un gran avance en la programación, tal como lo fue la programación estructurada en la década de los ’70. Entre sus ventajas más importante se encuentran un notable aumento de la productividad del programador y de la robustez de los programas.
Personalmente, el lenguaje orientado a objetos que más me agrada es Eiffel (además de sus características de orientación a objetos, soporta la metodología de diseño por contratos). Otro lenguaje muy bien diseñado (y de uso creciente) es Ruby. También es recomendable, y muy simple para comenzar, el lenguaje Python.
Apéndice: Un ejemplo en PHP
He agregado este apéndice siguiendo la sugerencia de un amigo (gracias, Santi) sobre la conveniencia de contar con un ejemplo concreto utilizando el lenguaje PHP, debido a que el mismo es usado por un gran número de personas sin una formación formal en programación.
El ejemplo inicial, usando programación procedural, sería como sigue:
<?
function inicializar($nombre, $apellido, $edad) {
$persona['nombre'] = $nombre;
$persona['apellido'] = $apellido;
$persona['edad'] = $edad;
return $persona;
}
function nombre_completo($persona) {
return $persona['nombre'] . ' ' .$persona['apellido'];
}
function es_mayor($persona) {
return $persona['edad'] >= 18;
}
$p = inicializar('Juan', 'Perez', 25);
echo nombre_completo($p);
if (es_mayor($p)) {
echo " es mayor de edad.n";
} else {
echo " es menor de edad.n";
}
?>
Inmediatamente notamos una gran diferencia respecto del ejemplo en Pascal: en PHP nunca definimos un tipo persona ni su estructura. Esto dificulta la comprensión del programa, ya que solamente inspeccionando la función inicializar podemos ver qué campos contiene el arreglo asociativo persona. Todo esto se debe a que PHP es un lenguaje con un sistema de tipos dinámico (una característica que lo hace muy simple y rápido para pequeños desarrollos, pero que se vuelve un arma de doble filo en programas complejos).
La versión orientada a objetos de este mismo programa sería la siguiente:
<?
class Persona {
var $nombre;
var $apellido;
var $edad;
function Persona($nombre, $apellido, $edad) {
$this->nombre = $nombre;
$this->apellido = $apellido;
$this->edad = $edad;
}function nombre_completo() {
return $this->nombre . ' ' . $this->apellido;
}function es_mayor($persona) {
return $this->edad >= 18;
}
}$p = new Persona('Juan', 'Perez', 25);
echo $p->nombre_completo();if ($p->es_mayor) {
echo " es mayor de edad.n";
} else {
echo " es menor de edad.n";
}?>
Este ejemplo nos permite introducir otro concepto de la POO: el uso de constructores. Un constructor es una función de una clase que se ejecuta automáticamente al crear un objeto (instancia). En el caso de PHP (y de varios otros lenguajes) el constructor debe tener el mismo nombre que la clase (en nuestro ejemplo, Persona).
La sentencia $p = new Persona(«Juan», «Perez», 25); crea un nuevo objeto ($p), de la clase Persona, ejecutando la función Persona (que reemplaza a la antigua función inicializar) con los parámetros «Juan», «Perez» y 25.
Otra diferencia respecto del ejemplo anterior, es la utilización del operador -> como selector de métodos y atributos de un objeto. En la definición de la clase Persona, $this es una referencia al objeto actual, necesaria para determinar el alcance de las variables utilizadas (para acceder al atributo nombre de la clase, debemos escribir $this->nombre, ya que $nombre sería interpretado como una nueva variable).
Si usted programa en PHP le recomiendo la utilización de POO (en particular, la versión 5 mejora mucho las características de orientación a objetos del lenguaje).
Apéndice: un ejemplo en Python
A continuación, un ejemplo de la versión orientada a objetos utilizando el lenguaje Python:
class Persona:def __init__(self, nombre, apellido, edad):
self.nombre = nombre
self.apellido = apellido
self.edad = edaddef nombre_completo(self):
return self.nombre + ' ' + self.apellidodef es_mayor(self):
return self.edad >= 18p = Persona('Juan', 'Perez', 25)
print p.nombre_completo(),
if p.es_mayor:
print "es mayor de edad."
else:
print "es menor de edad."
Como podemos apreciar, el constructor de la clase se declara como __init__, en tanto que la referencia al objeto actual se llama self (y debe aparecer como primer parámetro de todos los métodos de la clase, aunque no se utiliza en la invocación de los mismos).
Python es un lenguaje muy simple, pero caben aquí las mismas consideraciones hechas para PHP, respecto de los lenguajes con sistemas de tipos dinámicos.
Apéndice: un ejemplo en Eiffel
Finalmente, un ejemplo en mi lenguaje de programación orientado a objetos favorito: Eiffel. A continuación la definición de la clase persona:
class PERSONAcreation make
feature
nombre: STRING;
apellido: STRING;
edad: INTEGER;make(n, a: STRING; e: INTEGER) is
do
nombre := n
apellido := a
edad := e
endnombre_completo: STRING is
do
Result := nombre + " " + apellido
endes_mayor: BOOLEAN is
do
Result := edad >= 18
end
end
Debemos recordar que Eiffel (a diferencia de los demás lenguajes utilizados como ejemplo) es totalmente orientado a objetos (o puro, como suele decirse). Es por esto que no hay diferencia entre las clases y el «programa principal«, siendo este también una clase (llamada raíz o ROOT).
A continuación, el código de la clase ROOT:
class ROOTcreation make
feature
make is
local
p: PERSONA
do
create p.make("Juan", "Perez", 25)
print(p.nombre_completo)
if p.es_mayor then
print(" es mayor de edad.%N")
else
print(" es menor de edad.%N")
end
end
end
Podemos notar que los nombres de clases se escriben con mayúsculas, en tanto que el lenguaje permite especificar cuál es el constructor de cada clase (en este caso, el método make).
Vemos también que sin ver el código de la clase PERSONA es imposible saber si nombre_completo es un atributo (variable) o un método (función). Esto, como habíamos señalado al comienzo del artículo, es una ventaja (notese que en PHP y en Python no ocurre lo mismo), ya que podemos cambiar la implementación de la clase, sin alterar el resto del programa.
Al tener un sistema de tipos fuerte y estático, Eiffel nos obliga a declarar el tipo de cada variable o parámetro utilizado, permitiendo así que el compilador detecte posibles errores antes de la ejecución del programa.
Continuen con los artículos de programación que me resultan de mucha utilidad.
Saludos!
Gracias por este enfoque, les recomiendo un par de articulos relacionados a la POO y escritos por Miguel Angel Abian en http://www.javahispano.org
ampliamente recomendables.
Saludos.
Aquí la URL
————————————————-
http://www.javahispano.org/tutorials.type.action?type=is
Dos cositas:
Correcciones: falta un paréntesis final en el párrafo que le sigue a la definición de la clase PERSONA en Eiffel. Y sobra la primera coma en el párrafo que le sigue: «A, continuación,».
Pregunta: porqué el método Python «nombre_completo» requiere paréntesis en su invocación y el método «es_mayor» no? Siendo que ambos tienen como parámetro solamente a «self».
Grande Ricardo!!!
Los errores, corregidos.
Con respecto a la diferencia entre «nombre_completo» y «es_mayor» en Python, el problema radica en que la invocación al primero ocurre en una sentencia «print». De no colocar los paréntesis, el resultado es algo como lo que sigue:
bound method Persona.nombre_completo of __main__.Persona instance at 0xa7d40e2c
He aquí una muestra de las «pequeñas inconsistencias» de Python (por eso, entre otras cosas, no es mi lenguaje favorito).
Buenas
Interesante, ojala cuelges mas al respecto.
De momento java me parece que usa menos codigo que los lenguajes que has puesto en los ejemplos.
Saludos
Genial simplemente genial, es la mejor forma q he visto de plantear la POO , por fin vi la luz , ya estoy empleandolo en mis programas muchas ,muchas gracias . Un abrazo
Me parecio muy interesante el articulo por que diste un pantallaso de los distintos lenguaje.Personalmente solo conocia java ya que es el unico lenguaje que trabaje (con respecto al paradigma POO).Saludos.
Muy buen artículo! Mencionan dos tutoriales de Miguel Ángel Abián, sobre orientación a objetos en javaHispano, que son excelentes. Me han servido para completar ideas que tenía un poco difusas y para aprender sobre lenguajes OO.
Bravo por ustedes, porque si no jamás los habría descubierto.
Gracias otra vez y saludos.
necesito saber en que se ralciona microsoft con la oop.
ademas de que me parecio muy interesante la programacion orientada a objetos en muchos sentidos y deseo que me envien una respuesta acerda de como se relaciona microsoft con la oop.
>Con respecto a la diferencia entre >”nombre_completo” y ”es_mayor” en Python, el >problema radica en que la invocación al primero >ocurre en una sentencia ”print”. De no colocar >los paréntesis, el resultado es algo como lo que >sigue:
>bound method Persona.nombre_completo of >__main__.Persona instance at 0xa7d40e2c
>He aquí una muestra de las ”pequeñas >inconsistencias” de Python (por eso, entre otras >cosas, no es mi lenguaje favorito).
No hay tal inconsistencia (Python, de hecho, se distingue por su consistencia). Tanto ‘nombre_completo’ como ‘es_mayor’ son métodos, así que tienes que llamarlos usando paréntesis, sin ninguna excepción. Y esto es lo que te comentaba Ricardo, que en la línea:
if p.es_mayor:
te falta unos paréntesis, para quedar así:
if p.es_mayor():
si no, estás evaluando el propio método, lo que siempre resultará verdadero.
saludos
De verdad que agredezco mucho la explicación, estoy en pleno proceso de transición y me ayudó mucho esta información de verdad que gracias… y espero que nunca pierda la buena motivación de ayudarnos …
Me gustaria q cololocarn tipo documental q indeque como programar desde un inicio de los programas de pascal asta llegar a los mas avansados en este caso q fuera as ta java orientado a objetos
Excelente articulo, especialmente para los que como yo estamos iniciandonos en esto de la POO.
Saludos
El articulo está muy bien, espero que sigas profundizando sobre el tema
Saludos
Me gustaria que publicaran la diferencia que existe entre la POO y la programacion procedural y el ¿Porqué eliminaron los apuntadores en JAVA?
Excelente manual. Me ha ayudado mucho a comprender la POO.
Tal como te indicó jp, en python faltan los paréntesis en if p.es_mayor():
Saludos
«HOLA NECESITO BIBLIOGRAFIA DEL LENGUAJE DEPROGRAMACION SELF, LA NECESITO, POR FAVOR ESCRIBIRME A: ciberalex82yahoo.es
Muy interesante su articulo, pero quiero que hablaran sobre la programacion orientada a objetos y la microsoft, una vision hacia el futuro de la poo y en que se relacion con Borland.
Gerson
Mil gracias, impresionante…
Este articulo es una muy buena forma de explicar la POO, era lo que me faltaba para entender esta tipo de programación, gracias por tomarte la molestia de escribirlo.
HOLA NECESITO QUE HABLE SOBRE LA PROGRAMACION DORIENTADA A ESTRUCTURA
java no es puro
muy bueno che muy muy capo muy claro ojala fuera mi profesor en la facultad
puras pendejadas ay aki
Buenas que tal? Soy estudiante de Ingeniera en Informatica y ando buscando material basico como para arrancar con la Programacion Orientada a Objetos de C++.
Podria ser libros, tutoriales, lo que fuese.
Muchas gracias.
Saludos
Me interesa mucho la programación Orientada a Objetos, yo programo un poquito en C++, pero necsito conocer más sobre temas de COM, WinSock etc.
Gracias..
excelente articulo. Explicado de la mejor manera posible, la verdad como dijo un comentarista… «por fin vi la luz». Con gran simplesa logra hacer entender el paradigma de la POO
muy buena la info me ha sido de mucha ayuda, gracias
que paso con el codigo en .NET ,,, no sean racistas jajaja
Buen articulo, los errores se disculpan, no somos perfectos, podemos equivocarnos, en refrencia al tema, me guztaria saber donde encontrar tutoriales etc… de Eiffel y modelado de objetos sin UML, en castellano, no me guzta leer en ingles.
desde ya muchas gracias…
Excelente¡¡¡¡
He pasado por muchas paginas web, pero ninguna me ha aclarado tanto la POO como esta !!!!
Lo unico si es posible pediria mas información sobre herecias entre clases y quizas algun ejemplo de como seria el funcionamiento, y tambien si es posible cuando utilizar public private etc….
Muchisimas gracias.
F genial, todavia esta vigente, lo recomiendo.
No se como caí acá, pero me sirvió bastante. Se agradece.