La Programación orientada a Objetos (POO) es un paradigma de programación, es decir, un modelo o un estilo de programación que nos da unas guías sobre cómo trabajar con él. Se basa en el concepto de clases y objetos.
Con el paradigma de Programación Orientado a Objetos lo que buscamos es dejar de centrarnos en la lógica pura de los programas, para empezar a pensar en objetos, lo que constituye la base de este paradigma. Esto nos ayuda a poder modelar objetos de la vida real o abstractos con su comportamiento y características de forma directa hacia un lenguaje de programación.
Un programa orientado a objetos puede verse como: «Un conjunto de objetos que colaboran enviándose mensajes».
La POO introduce nuevos conceptos, que amplían los conceptos vistos hasta el momento.
Además de los conceptos previamente vistos, la POO incluye más conceptos como Herencia, Abstracción, Polimorfismo, Encapsulamiento, Acoplamiento y Cohesión que veremos mas adelante a medida que avancemos en la POO.
Para tratar de entender con ejemplos, podemos crear una clase para representar animales llamada "Animal" y tener una serie de atributos, como "nombre" y "edad", y una serie de tareas realizables (comportamiento) que estos animales pueden tener, como caminar o comer, y que a su vez se implementan como métodos en la clase (funciones).
Otro ejemplo, podemos tener una clase para representar vehículos, llamada "Vehiculo". Los atributos pueden ser patente, marca, modelo, etc. Y el comportamiento puede ser arrancar, poner alarma, acelerar, etc.
Como puede verse, cosas cotidianas como vehículos, animales o cualquier cosa que nos imaginemos pueden ser representadas en un lenguaje de programación mediante el paradigma POO.
Luego de esta breve explicación, parece ser que la POO es bastante lógica, pero no es algo que existió siempre en los lenguajes de programación y muchos de estos no lo soportan.
En parte surgió debido a las necesidades de los programadores, en el trascurso de los años, de hacer programas cada vez mas complejos.
En el mundo de la programación hay gran cantidad de aplicaciones que realizan tareas muy similares y es importante identificar los patrones que nos permiten no reinventar la rueda. La programación orientada a objetos intentaba resolver esto.
Uno de los primeros mecanismos que se han utilizado para agrupar bloques y resolver problemas repetitivos fueron las funciones. Si bien las funciones son muy útiles, no son suficientes para resolver problemas complejos por si solas.
Imaginemos que tenemos un juego de naves en 2D moviéndose por la pantalla. Cada una de ellas tendrá una una posición (x,y), un color, un tamaño de nave como también un comportamiento cada una.
Sin usar POO tendríamos que definir una variable por cada dato que queremos almacenar para cada nave. Si tenemos 10 naves y 5 datos para almacenar por cada nave, ¡Tendríamos que definir 50 variables!. El código sería un verdadero desorden y más aún si quisieramos modificar la cantidad de naves o agregar atributos nuevos.
Si diseñamos el programa con una arquitectura orientada a objetos esto sería mucho mas sencillo.
Mediante una clase podríamos definir lo general de las naves; sus atributos y comportamiento, luego "fabricar" naves (crear objetos) cada una con sus cualidades.
Como hemos mencionado anteriormente, una clase es como una plantilla para crear objetos. Imaginemos que queremos crear un objeto para representar un auto.
# Creamos una clase vacía
class Auto:
pass # No hace nada
Ahora que tenemos la clase, podemos crear un objeto de la misma. Podemos hacerlo como si de una variable normal se tratase;
nombre_variable=Clase(). Dentro de los paréntesis irían los parámetros de entrada si los hubiera.
mi_auto = Auto()
otro_auto = Auto()
Cuando se crea un objeto a partir de una clase se dice que se instancia una clase o que un objeto es instancia de una determinada clase.
En este ejemplo vimos una clase vacía, sin atributos ni comportamiento definido, lo cual no tiene ninguna utilidad práctica. Pero nos sirve para introducirnos en la sintáxis.
IMPORTANTE: Por convención, los nombres de las clases empiezan siempre en mayúsculas.
Las clases pueden tener atributos y es importante saber que hay dos tipos:
De ahora en mas diremos variables de instancia a los atributos de instancia y variables de clase a los atributos de clase.
Los lenguajes nos proveen un constructor por defecto para que cada clase pueda construir objetos. Pero a veces resulta muy útil tener nuestros propios constructores.
En Python hay un método especial llamado __init__ que se usa como constructor.
class Auto:
def __init__(self, un_modelo, un_color): # Constructor
self.modelo = un_modelo # Variable de instancia
self.color = un_color # Variable de instancia
mi_auto = Auto('Fiesta', 'Blanco') # Se instancia objeto
print(mi_auto.modelo, mi_auto.color) # Fiesta Blanco
otro_auto = Auto('Cruze', 'Negro') # Se instancia objeto
print(otro_auto.modelo, otro_auto.color) # Cruze Negro
Seguramente te hayas fijado en el self que se pasa como parámetro de entrada del método. Es una variable que representa la instancia de la clase, y deberá estar siempre ahí.
El uso de __init__ y el doble __ no es una coincidencia. Cuando veas un método con esa forma, significa que está reservado para un uso especial del lenguaje.
class Auto:
origen = "Argentina" # Variable de clase
def __init__(self, un_modelo): # Constructor
self.modelo= un_modelo # Variable de instancia
def arrancar(self): # Método
print("Auto encendido")
def acelerar(self, velocidad): # Método
print(f"Acelerando a {velocidad}km/h")
mi_auto = Auto('Cruze')
mi_auto.arrancar() # Auto encendido"
mi_auto.acelerar(40) # Acelerando a 40km/h
Podemos acceder a los métodos con un punto luego del identificador del objeto. El primer parámetro de los métodos debe ser self. Ver código
En Python las variables de clase se definen fuera de los métodos y las variables de instancia van dentro de algún método.
Queremos implementar en código el comportamiento de un semáforo.
El semáforo debe tener un estado de luz (Rojo o Verde) y de alguna forma deberíamos intercambiar entre un color de luz y el otro.
¿Cómo se les ocurre que podemos hacer?
Si bien hasta ahora no lo hemos notado, los atributos son objetos. Con esta afirmación podemos crear una clase y que una instancia de esa clase sea el atributo de otra clase que estemos trabajando.
class Estereo:
def encender(self):
# Código
def apagar(self):
# Código
# ... Más código ...
class Auto
def __init__(self, un_color): # Constructor
self.audio = Estereo() # Objeto como va de instancia
self.color = un_color
# ... Más código ...
De esta manera, podemos crear tantos objetos como creamos que sean necesarios. Y de hecho es bueno que hagamos una clase para cada objeto que contenga un comportamiento particular.
Crea una clase llamada Persona que tenga los siguientes atributos: nombre y edad. Luego, crea un método llamado "saludar" que imprima un mensaje de saludo que incluya el nombre y la edad de la persona.