Fundamentos de POO
Introducción a la Programación Orientada a Objetos
Comprende qué es la POO, qué problemas resuelve frente al paradigma procedural y cómo dar tus primeros pasos con Java.
Introducción a la Programación Orientada a Objetos
En una frase: la Programación Orientada a Objetos es una forma de diseñar software que organiza los datos y las acciones que operan sobre ellos en unidades coherentes llamadas objetos, facilitando la comprensión y evolución de sistemas complejos.
¿Qué aprenderás?
Al finalizar esta lección podrás:
- Explicar por qué surge la programación orientada a objetos y qué problemas resuelve frente a enfoques anteriores.
- Distinguir las diferencias conceptuales entre el paradigma procedural y el orientado a objetos.
- Conocer los tres paradigmas de programación más influyentes (procedural, orientado a objetos y funcional).
- Reconocer las partes esenciales de un programa en Java y cómo se ejecuta a través de su método
main. - Saber qué herramientas componen el entorno de desarrollo en Java (JDK, JVM e IDEs).
1. El problema que queremos resolver
Cuando empezamos a programar, solemos escribir instrucciones directas de arriba hacia abajo: declaramos variables sueltas, agregamos condicionales y escribimos funciones que transforman esos datos. Este estilo se conoce como programación procedural o estructurada.
Para un script de 30 líneas, este enfoque funciona de maravilla. Sin embargo, ¿qué sucede cuando la aplicación crece a miles de líneas y debe gestionar cientos de elementos?
Imagina un sistema para gestionar las cuentas de un banco:
- Necesitas guardar el nombre del cliente, su documento, su número de cuenta y su saldo.
- Si tienes 100 clientes, tendrías que coordinar 100 conjuntos de variables o arreglos paralelos.
- Cualquier función del programa tiene acceso libre para modificar el saldo directamente, sin pasar por reglas de validación (por ejemplo, permitir un retiro con saldo insuficiente o valores negativos).
- Si cambia la estructura de los datos (por ejemplo, dividir el nombre en nombre y apellido), tendrás que rastrear y modificar todas las funciones dispersas por el código que toquen esas variables.
A este fenómeno se le conoce como código frágil y fuertemente acoplado: los datos viajan desprotegidos por todo el sistema y cualquier cambio en un lugar puede romper partes imprevistas de la aplicación.
La Programación Orientada a Objetos (POO) nace para resolver esta dificultad agrupando los datos y las reglas que los gestionan en un único lugar seguro y predecible.
2. Explicación intuitiva
Piensa en cómo interactúas con los objetos del mundo real.
Cuando conduces un automóvil, no interactúas con los pistones del motor, la presión del combustible o el voltaje del alternador conectando cables manualmente. El automóvil te expone una interfaz clara: un volante, los pedales y una palanca. Al presionar el acelerador, el automóvil internamente sabe cómo aumentar las revoluciones y avanzar.
El automóvil es un objeto:
- Tiene un estado interno: cantidad de combustible, velocidad actual, encendido o apagado.
- Tiene comportamientos definidos: encender, acelerar, frenar, cambiar de marcha.
- Protege su mecanismo: no te deja alterar directamente la inyección de combustible con las manos mientras conduces; debes interactuar a través de sus controles seguros.
En la programación orientada a objetos hacemos exactamente lo mismo con los conceptos de nuestro software: en lugar de dejar datos sueltos expuestos a todo el programa, construimos piezas independientes que encapsulan su propia información y ofrecen operaciones claras para comunicarse con las demás.
3. Definición técnica y paradigmas
Un paradigma de programación es un modelo, enfoque o filosofía para estructurar y organizar la solución a un problema mediante código.
A lo largo de la historia de la computación han destacado tres paradigmas principales:
Los tres paradigmas principales
-
Paradigma Procedural (Estructurado):
- El programa se organiza como una serie de procedimientos, rutinas o funciones que se ejecutan paso a paso transformando datos que residen en variables separadas.
- Fortaleza: directo, sencillo para programas pequeños o cálculos matemáticos lineales.
- Desafío: al crecer, la separación entre datos y funciones genera dispersión y dificultad de mantenimiento.
-
Paradigma Orientado a Objetos (POO):
- El programa se concibe como una red de objetos que colaboran entre sí enviándose mensajes. Cada objeto es responsable de su propio estado y de las operaciones que realiza.
- Fortaleza: modularidad, reutilización, modelado intuitivo de problemas complejos y facilidad de mantenimiento.
- Desafío: requiere mayor planificación inicial y puede introducir sobrecarga si se utiliza para tareas excesivamente triviales.
-
Paradigma Funcional:
- El programa se estructura evaluando funciones matemáticas puras, evitando cambiar el estado global y priorizando la inmutabilidad de los datos.
- Fortaleza: previene efectos secundarios y facilita el procesamiento concurrente.
- Uso común: procesamiento de flujos de datos, análisis matemático y transformaciones.
Nota: Los lenguajes modernos (incluido Java) son multiparadigma: aunque su núcleo es orientado a objetos, incorporan características funcionales (como expresiones lambda y Streams) para combinar lo mejor de cada enfoque.
4. Representación visual
Observa la diferencia entre tener datos dispersos accedidos por múltiples funciones frente a agrupar datos y operaciones dentro de objetos autónomos:
flowchart TD
subgraph Procedural["Enfoque Procedural (Datos separados de las funciones)"]
D1["Datos sueltos: saldo, titular, cuenta"]
F1["Funcion depositar()"]
F2["Funcion retirar()"]
F3["Funcion transferir()"]
D1 --> F1
D1 --> F2
D1 --> F3
end
subgraph OO["Enfoque Orientado a Objetos (Unidades autonomas)"]
subgraph ObjCuenta1["Objeto: Cuenta de Carlos"]
Estado1["Estado: titular, saldo"]
Met1["Metodos: depositar(), retirar()"]
end
subgraph ObjCuenta2["Objeto: Cuenta de Elena"]
Estado2["Estado: titular, saldo"]
Met2["Metodos: depositar(), retirar()"]
end
ObjCuenta1 <-->|"Colaboran mediante mensajes"| ObjCuenta2
end
Cómo interpretar el diagrama:
- En el enfoque procedural, los datos no tienen dueño: cualquier función puede leerlos o modificarlos, lo que dificulta saber quién o cuándo provocó un valor incorrecto.
- En el enfoque orientado a objetos, cada cuenta protege su estado interno y solo cambia cuando se invoca una de sus operaciones autorizadas.
5. Ejemplo inicial: el problema en el enfoque procedural
Veamos qué ocurre si gestionamos cuentas bancarias únicamente con variables y funciones independientes en Java:
public class EjemploProcedural {
public static void main(String[] args) {
// Datos de la cuenta 1
String titular1 = "Carlos";
double saldo1 = 1000.0;
// Datos de la cuenta 2
String titular2 = "Elena";
double saldo2 = 500.0;
// Cualquier parte del código puede modificar variables sin validación
saldo1 -= 2000.0; // ¡Saldo negativo no controlado!
System.out.println("Saldo de " + titular1 + ": " + saldo1);
System.out.println("Saldo de " + titular2 + ": " + saldo2);
}
}
Saldo de Carlos: -1000.0
Saldo de Elena: 500.0
¿Qué problemas observamos?
- Falta de protección de datos: nadie impidió que el saldo de Carlos se volviera negativo.
- Escalabilidad nula: si necesitamos agregar una tercera cuenta, debemos crear
titular3ysaldo3. Con 100 cuentas, el código se vuelve inmanejable. - Lógica duplicada: si queremos aplicar una regla (por ejemplo: «no permitir retiros mayores al saldo disponible»), tendríamos que recordar escribir ese
ifen cada lugar donde se reste dinero.
6. Primer vistazo al enfoque orientado a objetos y estructura en Java
En la POO, creamos un molde (la clase) que define los datos que tendrá cada cuenta y las operaciones que sabe realizar. Luego creamos instancias concretas (los objetos) a partir de ese molde:
// 1. Definición del molde (Clase)
class CuentaBancaria {
String titular;
double saldo;
// Constructor: prepara los datos iniciales al crear la cuenta
CuentaBancaria(String titularInicial, double saldoInicial) {
titular = titularInicial;
saldo = saldoInicial;
}
// Comportamiento: depositar dinero aplicando validaciones
void depositar(double monto) {
if (monto > 0) {
saldo += monto;
}
}
// Comportamiento: retirar dinero asegurando saldo suficiente
void retirar(double monto) {
if (monto > 0 && saldo >= monto) {
saldo -= monto;
} else {
System.out.println("Operación rechazada: saldo insuficiente en la cuenta de " + titular);
}
}
// Comportamiento: mostrar la información de la cuenta
void mostrarEstado() {
System.out.println("Titular: " + titular + " | Saldo: $" + saldo);
}
}
// 2. Punto de entrada de la aplicación
public class EjemploOrientadoAObjetos {
public static void main(String[] args) {
// Creamos dos objetos independientes a partir de la misma clase
CuentaBancaria cuentaCarlos = new CuentaBancaria("Carlos", 1000.0);
CuentaBancaria cuentaElena = new CuentaBancaria("Elena", 500.0);
// Operamos a través de los métodos definidos
cuentaCarlos.retirar(2000.0); // Se valida y se rechaza
cuentaCarlos.retirar(200.0); // Se valida y se aprueba
cuentaElena.depositar(150.0); // Se incrementa el saldo de Elena
cuentaCarlos.mostrarEstado();
cuentaElena.mostrarEstado();
}
}
Resultado de la ejecución
Operación rechazada: saldo insuficiente en la cuenta de Carlos
Titular: Carlos | Saldo: $800.0
Titular: Elena | Saldo: $650.0
Anatomía mínima de un programa Java
Para comprender el código anterior, recordemos los tres elementos fundamentales que todo programa Java posee:
- La clase contenedora (
public class EjemploOrientadoAObjetos): en Java, todo código ejecutable reside dentro de una clase. El nombre del archivo debe coincidir exactamente con el de la clase pública (EjemploOrientadoAObjetos.java). - El método
main: es el punto de entrada que la máquina virtual busca para iniciar la ejecución del programa:public static void main(String[] args)public: accesible desde cualquier punto del entorno.static: pertenece a la clase y no requiere crear una instancia previa para ejecutarse.void: no devuelve ningún valor al finalizar.String[] args: arreglo de cadenas que permite recibir argumentos desde la línea de comandos.
- Instanciación con
new: el operadornew CuentaBancaria(...)reserva memoria para un nuevo objeto y ejecuta su constructor.
7. Comparación entre enfoques
| Criterio | Paradigma Procedural | Paradigma Orientado a Objetos |
|---|---|---|
| Eje central | Funciones y algoritmos secuenciales. | Objetos que encapsulan estado y comportamiento. |
| Organización de los datos | Variables dispersas o arreglos globales/locales. | Atributos protegidos dentro de cada instancia. |
| Protección del estado | Baja: cualquier función puede alterar los datos. | Alta: las modificaciones pasan por métodos controlados. |
| Reutilización | Reutiliza funciones individuales. | Reutiliza clases completas y modelos de dominio. |
| Mantenimiento en proyectos grandes | Complejo: cambios en datos afectan múltiples funciones. | Modular: los cambios internos de un objeto no rompen a otros. |
8. ¿Cuándo utilizar la POO y cuándo tener cuidado?
Es especialmente útil cuando:
- El software modela entidades del mundo real o del negocio con reglas específicas (usuarios, pedidos, productos, facturas).
- El sistema cuenta con múltiples elementos que comparten estructura pero tienen datos independientes.
- El proyecto crecerá en el tiempo y será mantenido por un equipo de desarrolladores.
- Se requiere que el código sea testeable de forma modular.
Conviene tener precaución cuando:
- La tarea a resolver es un script muy pequeño y puntual (por ejemplo, renombrar 5 archivos en disco o calcular un promedio único).
- Se diseñan jerarquías de clases artificiales antes de comprender el problema real (sobreingeniería).
9. Preparación del entorno de desarrollo en Java
Para escribir y ejecutar tus programas en Java necesitas dos herramientas fundamentales:
-
JDK (Java Development Kit):
- Es el kit de herramientas oficial de desarrollo. Incluye el compilador (
javac), las librerías base de Java y la máquina virtual (JVM, Java Virtual Machine) que ejecuta el código compilado (bytecode). - Se recomienda utilizar una versión moderna LTS de Java (como Java 17 o Java 21).
- Es el kit de herramientas oficial de desarrollo. Incluye el compilador (
-
Un Entorno de Desarrollo Integrado (IDE):
- Aunque se puede programar en un bloc de notas y compilar desde la terminal con
javac, un IDE facilita enormemente el aprendizaje detectando errores en tiempo real y ofreciendo herramientas de depuración y ejecución con un clic. - Opciones recomendadas:
- IntelliJ IDEA (Community Edition): moderno, potente y estándar en la industria.
- Apache NetBeans: muy utilizado en entornos educativos universitarios.
- Visual Studio Code: ligero y extensible mediante el Extension Pack for Java.
- Aunque se puede programar en un bloc de notas y compilar desde la terminal con
10. Errores frecuentes al iniciarse en la POO
1. Tratar a las clases como simples contenedores de funciones globales
Qué sucede: crear clases repletas de métodos static y variables globales, programando en estilo procedural pero dentro de un archivo .java.
Cómo evitarlo: recuerda que una clase debe representar un concepto con identidad y estado propio.
2. Olvidar que cada objeto mantiene su propia copia de datos
Qué sucede: modificar el saldo de cuentaCarlos y esperar que mágicamente afecte a cuentaElena.
Cómo evitarlo: cada vez que utilizas new, se crea un espacio de memoria independiente para ese objeto en particular.
3. Crear clases complejas para datos que no requieren comportamiento
Qué sucede: crear una jerarquía de 5 clases para un cálculo que requería dos operaciones matemáticas sencillas.
Cómo evitarlo: aplica el principio de simplicidad; introduce clases cuando haya un concepto que justifique agrupar datos y operaciones.
11. Ejercicio práctico
Enunciado
Diseña una clase llamada Termometro que modele un instrumento para medir temperatura en grados Celsius.
Requisitos:
- Debe guardar la temperatura actual como un valor numérico decimal (
double). - Debe inicializarse con una temperatura inicial a través de su constructor.
- Debe incluir un método
ajustarTemperatura(double nuevaTemperatura)que solo acepte valores físicamente posibles (por ejemplo, temperaturas no inferiores al cero absoluto:-273.15 °C). Si el valor es menor, debe mostrar un mensaje de aviso y no modificar la temperatura. - Debe incluir un método
mostrarLectura()que imprima la temperatura actual en consola. - En una clase ejecutable con método
main, crea dos termómetros (uno para el interior y otro para el exterior), actualiza sus valores y muestra sus lecturas.
Pistas
Mostrar pistas
- Declara un atributo
double temperatura;dentro de la claseTermometro. - El constructor debe recibir
double temperaturaInicialy asignarlo atemperatura. - En
ajustarTemperatura, usa un condicionalif (nuevaTemperatura >= -273.15)para validar el valor antes de guardarlo.
Solución propuesta
Mostrar solución y explicación
// Clase que modela el termómetro
class Termometro {
double temperatura;
Termometro(double temperaturaInicial) {
if (temperaturaInicial >= -273.15) {
temperatura = temperaturaInicial;
} else {
temperatura = 0.0;
System.out.println("Temperatura inicial no válida. Se asigna 0.0 °C.");
}
}
void ajustarTemperatura(double nuevaTemperatura) {
if (nuevaTemperatura >= -273.15) {
temperatura = nuevaTemperatura;
} else {
System.out.println("Error: " + nuevaTemperatura + " °C está por debajo del cero absoluto (-273.15 °C).");
}
}
void mostrarLectura(String ubicacion) {
System.out.println("Termómetro [" + ubicacion + "]: " + temperatura + " °C");
}
}
// Punto de entrada del programa
public class PruebaTermometro {
public static void main(String[] args) {
Termometro termometroInterior = new Termometro(21.5);
Termometro termometroExterior = new Termometro(14.0);
// Modificamos temperaturas
termometroInterior.ajustarTemperatura(23.0);
termometroExterior.ajustarTemperatura(-300.0); // Intento no válido
// Mostramos lecturas finales
termometroInterior.mostrarLectura("Sala");
termometroExterior.mostrarLectura("Patio");
}
}
Resultado esperado:
Error: -300.0 °C está por debajo del cero absoluto (-273.15 °C).
Termómetro [Sala]: 23.0 °C
Termómetro [Patio]: 14.0 °C
Explicación:
La clase Termometro protege el atributo temperatura garantizando que no contenga valores físicamente imposibles. Cada objeto (termometroInterior y termometroExterior) conserva su propio estado de forma totalmente independiente.
12. Resumen de la lección
- Propósito: la POO nace para superar las limitaciones del software procedural cuando crece, agrupando datos y operaciones en unidades coherentes y protegidas.
- Concepto clave: un objeto une estado (datos) y comportamiento (métodos), interactuando con otros objetos mediante llamadas a sus métodos.
- Java como lenguaje: todo programa Java se organiza en clases, requiere un método
maincomo punto de partida y se ejecuta sobre la Máquina Virtual de Java (JVM). - Herramientas base: para desarrollar en Java se requiere el JDK (compilador y JVM) y preferiblemente un IDE como IntelliJ IDEA, NetBeans o VS Code.
13. Comprueba tu comprensión
Responde estas preguntas para evaluar lo aprendido en esta lección:
- ¿Cuál es el principal problema de mantener los datos separados de las funciones en aplicaciones que crecen en tamaño?
- Si creas dos cuentas bancarias usando
new CuentaBancaria(...), ¿por qué retirar dinero de una no altera el saldo de la otra? - ¿Cuál es la función del método
public static void main(String[] args)en una aplicación Java? - ¿En qué se diferencia el rol del compilador
javacdel rol de la JVM al ejecutar un programa en Java?
14. Continúa aprendiendo
Ahora que comprendes qué motiva a la POO y cómo se estructura un programa básico en Java, el siguiente paso es profundizar en cómo se definen las clases y cómo interactúan los objetos en memoria:
- Siguiente lección recomendada: Clases y objetos — donde aprenderás a diseñar tu primera clase en Java (
Pedido), gestionar el estado independiente de múltiples objetos y usar constructores. - Categoría principal: Fundamentos de POO
15. Fuentes y lecturas recomendadas
- Oracle. The Java Tutorials: Trails Covering the Basics — Object-Oriented Programming Concepts. docs.oracle.com/javase/tutorial/java/concepts/
- Cay S. Horstmann. Core Java Volume I: Fundamentals (12th Edition). Pearson, 2022. Capítulo 4: «Objects and Classes».
- Joshua Bloch. Effective Java (3rd Edition). Addison-Wesley, 2018. Capítulo 4: «Classes and Interfaces».