Objetivo de aprendizaje
Al concluir este tema, el estudiante será capaz de:
- Explicar por qué un modelo de software conserva solo lo relevante para un propósito (abstracción) y justificar qué se conserva y qué se descarta.
- Traducir el enunciado de un problema en candidatos a clases, atributos y comportamientos, y decidir con criterios explícitos cuál es cuál.
- Asignar responsabilidades a las clases mediante fichas CRC y el principio del experto en información.
- Distinguir una relación de herencia («es un») de una de composición o asociación («tiene un», «usa»).
- Construir de manera incremental una solución en Python y probarla con escenarios derivados del enunciado.
Ruta de trabajo del tema
| Etapa | Contenido | Producto del estudiante |
|---|---|---|
| 1 | Planteamiento inicial | Hipótesis escrita sobre cómo empezar |
| 2 | Modelar es decidir qué conservar | Modelos distintos para propósitos distintos |
| 3 | Las piezas de la programación orientada a objetos | Vocabulario para traducir el problema |
| 4 | El método de seis pasos | Receta de trabajo |
| 5 | Aplicación guiada: la biblioteca de la facultad | Un análisis completo, paso a paso |
| 6 | Del diseño al código | Programa construido por versiones y probado |
| 7 | Un segundo caso para practicar | Análisis propio del estacionamiento |
| 8 | Verificación y reformulación | Cuestionario y respuesta revisada |
1. Planteamiento inicial
Quien comienza a programar suele recibir problemas ya descompuestos: «escriba una función que sume dos números». Los problemas reales llegan de otra manera: como un texto, escrito por una persona que conoce su oficio y no conoce la programación. Considere el siguiente enunciado.
Enunciado: la biblioteca de la facultad. La biblioteca desea controlar el préstamo de sus materiales.
R1. Cada material tiene un título y un código de inventario; puede ser un libro (que además tiene un autor) o una revista (que además tiene un número).
R2. Los socios, que pueden ser estudiantes o docentes, se identifican con una matrícula o un número de empleado y tienen un nombre.
R3. Un socio puede tener a la vez, como máximo, tres materiales prestados.
R4. Un material que está prestado no puede prestarse de nuevo hasta que se devuelva.
R5. Cada préstamo registra el material, el socio y el día de entrega; el plazo es de siete días.
R6. Si el material se devuelve después del plazo, se cobra una multa de 5 pesos por cada día de retraso.
R7. El personal quiere consultar qué materiales están disponibles y qué tiene prestado cada socio.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Esta hipótesis se retomará al final del tema.
2. Modelar es decidir qué conservar
Definición — Modelo: representación simplificada de una parte de la realidad, construida con un propósito. Abstracción es el proceso de seleccionar los aspectos relevantes para ese propósito y omitir el resto.
Una persona real tiene miles de rasgos: estatura, historial médico, gustos, calificaciones, amistades. Ningún programa los representa todos, y ninguno debería hacerlo. Cada rasgo conservado debe almacenarse, validarse, protegerse y mantenerse; cada rasgo innecesario es costo sin beneficio.
El propósito determina el modelo. La misma persona es, para la biblioteca, un socio con matrícula y préstamos; para el servicio médico, un paciente con alergias y tipo de sangre; para el control escolar, un estudiante con materias y promedio. Ninguno de los tres modelos es el «correcto»: cada uno es adecuado para su problema.
Realice la siguiente práctica. Elija un propósito, marque los datos que conservaría y compare con la propuesta. Después cambie el propósito y observe qué cambia.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Nota: si dos personas diseñan el mismo sistema y conservan datos distintos, no necesariamente alguna se equivoca; es probable que hayan entendido el propósito de manera distinta. Por eso el primer paso del método es delimitar el problema.
3. Las piezas de la programación orientada a objetos
En el paradigma orientado a objetos, un programa es un conjunto de objetos que colaboran enviándose mensajes (es decir, invocando métodos). El vocabulario básico es el siguiente.
| Pieza | Definición breve | Pregunta que ayuda a identificarla |
|---|---|---|
| Objeto | Entidad con identidad, estado (datos) y comportamiento (operaciones) | ¿Hay algo concreto que se distinga de otros, aunque se parezcan? |
| Clase | Plantilla que describe a un conjunto de objetos con los mismos atributos y operaciones | ¿Existen varios elementos de la misma categoría con el mismo tipo de datos y reglas? |
| Atributo | Dato que describe el estado de un objeto | ¿Es un valor que describe a otra cosa y no hace nada por sí mismo? |
| Método | Operación que un objeto sabe realizar o consultar | ¿Es una acción o una consulta que se hace sobre un objeto? |
| Relación | Vínculo entre clases: herencia, composición o asociación | ¿«Es un tipo de», «tiene» o «usa» a otra? |
| Invariante | Condición que debe cumplirse siempre sobre el estado de un objeto | ¿Qué estado nunca debe llegar a existir? |
La traducción del enunciado a estas piezas sigue una correspondencia general:
| En el enunciado aparece… | En el modelo suele corresponder a… |
|---|---|
| Un sustantivo que designa cosas con identidad y reglas propias («préstamo», «socio») | Una clase |
| Un sustantivo que solo describe a otro («título», «matrícula») | Un atributo |
| Un verbo o una consulta («prestar», «devolver», «calcular la multa») | Un método |
| «Es un tipo de», «puede ser un… o un…» | Herencia |
| «Tiene», «se compone de», «usa» | Composición o asociación |
| Una regla («como máximo tres», «no puede prestarse de nuevo») | Una validación dentro de un método y, en su conjunto, un invariante |
Importante: esta correspondencia es una guía para generar candidatos, no un algoritmo. Cada candidato debe pasar por una decisión razonada: no todo sustantivo es una clase ni todo verbo es un método.
4. El método de seis pasos
Los diseñadores de software no dependen de la inspiración; siguen una secuencia de preguntas. La siguiente es una versión adecuada para el nivel de este curso.
| Paso | Qué se hace | Pregunta guía | Producto |
|---|---|---|---|
| 1. Delimitar | Establecer el propósito, numerar los requisitos y decidir qué queda fuera | ¿Qué debe poder hacer el sistema y qué no? | Lista numerada de requisitos |
| 2. Identificar candidatos | Subrayar sustantivos y verbos del enunciado | ¿Qué cosas y qué acciones se mencionan? | Tabla de candidatos |
| 3. Decidir su papel | Clasificar cada candidato como clase, atributo, cálculo o fuera del modelo | ¿Tiene identidad y reglas propias, o solo describe a otra cosa? | Clases con sus atributos |
| 4. Asignar responsabilidades | Repartir los métodos entre las clases con fichas CRC | ¿Quién conoce los datos necesarios para hacerlo? | Fichas CRC |
| 5. Relacionar y proteger | Establecer herencia, composición y asociaciones; enumerar invariantes | ¿«Es un» o «tiene un»? ¿Qué estado nunca debe existir? | Diagrama de clases e invariantes |
| 6. Construir y probar | Programar por versiones y verificar con escenarios del enunciado | ¿Cada requisito se comprueba con un caso concreto? | Código y escenarios probados |
Nota: el proceso es iterativo. Al programar se descubre que una clase sobraba o que una responsabilidad estaba mal ubicada, y se vuelve a un paso anterior. Eso no es un fracaso: es la manera normal de trabajar.
5. Aplicación guiada: la biblioteca de la facultad
Paso 1. Delimitar el problema
Propósito: controlar qué materiales están prestados, a quién y con qué consecuencias si se devuelven tarde.
Requisitos: R1 a R7 del enunciado de la sección 1. Para evitar una falsa precisión, se supone que las fechas se representan con números de día (día 1, día 2, …) y no con calendarios reales.
Queda fuera del alcance: búsqueda por tema o autor, reservaciones, cobro real de multas, renovaciones, datos de contacto del personal. Declarar lo que queda fuera es tan importante como declarar lo que entra: protege contra el crecimiento sin límite del modelo.
Paso 2. Identificar candidatos
Se subrayan los sustantivos y los verbos del enunciado.
| Candidato | Tipo de palabra | Aparece en |
|---|---|---|
| biblioteca, material, libro, revista, socio, préstamo, personal | Sustantivo (cosas) | R1 a R7 |
| título, código de inventario, autor, número, matrícula, nombre, día de entrega | Sustantivo (datos) | R1, R2, R5 |
| multa, plazo, días de retraso, límite de tres | Sustantivo (valores o reglas) | R3, R5, R6 |
| prestar, devolver, cobrar multa, consultar disponibles, consultar préstamos | Verbo o consulta | R3 a R7 |
Paso 3. Decidir el papel de cada candidato
Para cada sustantivo se aplican tres preguntas, en este orden:
- ¿Tiene identidad propia y reglas o comportamiento? Si es así, es candidato a clase.
- ¿Es un valor que describe a otra cosa? Entonces es un atributo de esa otra cosa.
- ¿Puede obtenerse a partir de otros datos? Entonces es un cálculo (método o propiedad), y guardarlo como dato corre el riesgo de que quede desactualizado.
Si ninguna de las tres aplica, el candidato probablemente queda fuera del modelo. Clasifique los candidatos del enunciado y lea la razón de cada decisión.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Resultado del análisis:
| Clase | Atributos | Decisiones notables |
|---|---|---|
| Material | codigo, titulo, disponible |
Agrupa lo común a libros y revistas |
| Libro (especializa Material) | autor |
Solo agrega un dato |
| Revista (especializa Material) | numero |
Solo agrega un dato |
| Socio | matricula, nombre, prestamos, LIMITE |
Estudiante y docente no son subclases: solo difieren en un dato |
| Prestamo | material, socio, vence, PLAZO, MULTA_DIA |
La multa no se guarda: se calcula |
| Biblioteca | activos (y la colección) |
Coordina las operaciones entre las demás clases |
Error común: crear una clase por cada sustantivo. «Pesos» y «personal» no son clases en este problema, y «estudiante» frente a «docente» solo justificaría subclases si se comportaran de manera distinta.
Paso 4. Asignar responsabilidades
Cada verbo del enunciado debe terminar en un método de alguna clase. La pregunta es cuál. Un criterio sólido, conocido como principio del experto en información, dice: la responsabilidad se asigna a la clase que posee los datos necesarios para cumplirla. Así los objetos no necesitan pedir datos a otros para hacer su trabajo, y los cambios quedan localizados.
Asigne las responsabilidades siguientes.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Una ficha CRC (Clase, Responsabilidades, Colaboradores) resume cada clase en tres campos: su nombre, lo que sabe y hace, y las clases con las que trabaja para lograrlo. Complete las fichas de las cuatro clases principales con sus propias palabras y compare con la propuesta.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Paso 5. Establecer las relaciones y proteger los invariantes
Para decidir entre herencia y composición se aplica una prueba sencilla: «¿A es un B?» debe ser verdadero en todos los casos del problema para que haya herencia. Si lo que se afirma es «A tiene un B» o «A usa un B», corresponde composición o asociación.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
El diagrama de clases resultante es el siguiente (la flecha con triángulo indica herencia; el rombo, que una clase contiene a otras; la flecha simple, que una clase se relaciona con otra).
classDiagram
class Material {
+codigo
+titulo
+disponible
+prestar()
+devolver()
}
class Libro {
+autor
}
class Revista {
+numero
}
class Socio {
+matricula
+nombre
+prestamos
+puede_pedir()
}
class Prestamo {
+material
+socio
+vence
+multa(dia)
}
class Biblioteca {
+activos
+prestar(material, socio, dia)
+devolver(prestamo, dia)
}
Material <|-- Libro
Material <|-- Revista
Biblioteca o-- Socio
Biblioteca o-- Material
Prestamo --> Material
Prestamo --> Socio
Socio --> Prestamo
Los invariantes son las condiciones que el diseño debe garantizar siempre. Se obtienen de las reglas del enunciado:
| Invariante | Origen | Dónde se protege |
|---|---|---|
| Un material prestado no se presta de nuevo | R4 | Biblioteca.prestar (consulta Material.disponible) |
| Un socio nunca tiene más de tres préstamos vigentes | R3 | Socio.puede_pedir, consultado por Biblioteca.prestar |
| La multa nunca es negativa | R6 | Prestamo.multa |
Definición — Invariante: condición sobre el estado de un objeto que debe ser verdadera antes y después de cada operación pública. Diseñar buscando proteger los invariantes es la razón de ser del encapsulamiento, que se estudia en el tema 3.1.
6. Del diseño al código
El código se construye por versiones, cada una verificable. Así, cuando algo falla, el error se encuentra en el último paso agregado.
Versión 0. El punto de partida sin clases
Antes de usar clases conviene ver qué ocurre sin ellas. Ejecute el programa y observe el estado del diccionario después de la última modificación.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Los datos y las operaciones viven en lugares distintos y nada obliga a usarlos juntos. Esa separación es la que las clases resuelven.
Versión 1. Las clases con sus atributos
Se transcribe el resultado del paso 3. El código está documentado con comentarios; después de él, una lectura guiada explica cada fragmento.
# Clase Material: lo que tienen en común libros y revistas.
class Material:
def __init__(self, codigo, titulo): # constructor: se ejecuta al crear un objeto
self.codigo = codigo # atributo: identifica al material
self.titulo = titulo # atributo: describe al material
self.disponible = True # estado inicial: nadie lo tiene prestado
# Clase Socio: persona que puede pedir materiales prestados.
class Socio:
def __init__(self, matricula, nombre):
self.matricula = matricula
self.nombre = nombre
self.prestamos = [] # lista (inicialmente vacía) de sus préstamos
m = Material(101, "Fisica I") # se crea un objeto de la clase Material
s = Socio(2024001, "Ana") # se crea un objeto de la clase Socio
print(m.titulo, s.nombre)
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Compruebe el resultado en la corrida y observe que cada llamada a la clase crea un objeto con su propio estado.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Versión 2. Las responsabilidades como métodos
Se incorporan las reglas del paso 4 en la clase que posee los datos. La regla R4 («un material prestado no puede prestarse de nuevo») se convierte en una validación dentro de Material.
class Material:
def __init__(self, codigo, titulo):
self.codigo = codigo
self.titulo = titulo
self.disponible = True
def prestar(self):
# R4: un material prestado no puede prestarse de nuevo
if not self.disponible:
raise ValueError("El material ya esta prestado") # se rechaza la operación
self.disponible = False
def devolver(self):
self.disponible = True
m = Material(101, "Fisica I")
m.prestar()
print(m.disponible) # False
m.devolver()
print(m.disponible) # True
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Versión 3. Las clases colaboran: el escenario completo
Con las cinco clases definidas, el programa reproduce el escenario del enunciado: un socio pide un material el día 1 y lo devuelve el día 12.
class Material: # --- Material ---------------------------
def __init__(self, codigo, titulo):
self.codigo = codigo
self.titulo = titulo
self.disponible = True
class Socio: # --- Socio ------------------------------
LIMITE = 3 # atributo de CLASE: la regla R3
def __init__(self, matricula, nombre):
self.matricula = matricula
self.nombre = nombre
self.prestamos = []
def puede_pedir(self):
return len(self.prestamos) < Socio.LIMITE # R3: como máximo tres a la vez
class Prestamo: # --- Prestamo ---------------------------
PLAZO = 7 # R5
MULTA_DIA = 5 # R6
def __init__(self, material, socio, dia):
self.material = material
self.socio = socio
self.vence = dia + Prestamo.PLAZO # día límite de devolución
def multa(self, dia): # la multa se CALCULA, no se guarda
retraso = dia - self.vence
if retraso > 0:
return retraso * Prestamo.MULTA_DIA
return 0
class Biblioteca: # --- Biblioteca (coordinadora) ----------
def __init__(self):
self.activos = []
def prestar(self, material, socio, dia):
if not material.disponible: # R4
raise ValueError("Material no disponible")
if not socio.puede_pedir(): # R3
raise ValueError("El socio llego al limite")
p = Prestamo(material, socio, dia) # se crea el préstamo
material.disponible = False # se actualizan los estados
socio.prestamos.append(p)
self.activos.append(p)
return p
def devolver(self, prestamo, dia):
prestamo.material.disponible = True
prestamo.socio.prestamos.remove(prestamo)
self.activos.remove(prestamo)
return prestamo.multa(dia) # R6
b = Biblioteca() # --- Escenario --------------------------
m = Material(101, "Fisica I")
s = Socio(2024001, "Ana")
p = b.prestar(m, s, 1) # día 1
print("multa:", b.devolver(p, 12)) # día 12: 4 días de retraso
print(m.disponible, len(s.prestamos))
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Antes de ejecutar el programa, anticipe su salida. Active «Predecir la salida» y también «Predecir el método que se ejecuta», que pide identificar cuál implementación se ejecutará en cada llamada a un método.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Probar con escenarios
Un diseño no se valida por cómo luce, sino por lo que produce en casos concretos derivados de los requisitos. Cada requisito debería tener al menos un escenario.
| Escenario | Qué se hace | Resultado esperado | Requisito |
|---|---|---|---|
| E1 | Prestar un material disponible | Queda no disponible; el socio tiene 1 préstamo | R4, R5 |
| E2 | Prestar el mismo material otra vez | Error «Material no disponible» | R4 |
| E3 | Devolver el día 8 (el último del plazo) | Multa de 0 pesos | R5, R6 |
| E4 | Devolver el día 12 | Multa de 20 pesos | R6 |
| E5 | Pedir un cuarto material con tres vigentes | Error «El socio llego al limite» | R3 |
Utilice la pestaña «Mi programa» de la corrida para escribir sus propios escenarios con las clases anteriores y compruebe cada uno.
7. Un segundo caso para practicar: el estacionamiento
El método se aprende aplicándolo. Resuelva este problema completo, sin ayuda del ejemplo, y compare después su análisis con el de una persona compañera.
Enunciado: el estacionamiento de la facultad. El estacionamiento tiene 40 lugares. Cada vehículo se identifica por su placa y es un automóvil o una motocicleta. Una motocicleta ocupa un lugar y paga 10 pesos por hora; un automóvil ocupa un lugar y paga 20 pesos por hora. Cuando un vehículo entra, se le asigna el primer lugar libre y se registra la hora de entrada; si no hay lugares libres, se rechaza. Al salir, se calcula el importe por las horas transcurridas (cada fracción de hora se cobra completa) y se libera el lugar. Se desea saber en cualquier momento cuántos lugares libres hay.
Aplique los seis pasos y registre su tabla de candidatos con la decisión que tomó para cada uno.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Nota: el caso tiene una decisión deliberadamente discutible: ¿la tarifa distinta justifica dos subclases de
Vehiculo? La respuesta depende de si habrá otro comportamiento distinto además de la tarifa. Lo importante es argumentar la decisión con los criterios del paso 3.
8. Errores de diseño frecuentes
Error común — una clase por sustantivo: crear clases para valores simples («Pesos», «Titulo») o para actores que el sistema no modela. Resultado: muchas clases sin comportamiento.
Error común — la clase todopoderosa: concentrar todo en una clase «Sistema» o «Manager» que conoce y modifica los datos de las demás. Los demás objetos quedan convertidos en simples contenedores de datos, como el diccionario de la versión 0.
Error común — herencia por conveniencia: hacer que
Bibliotecaherede deListasolo para reutilizarappend. La herencia afirma una relación «es un»; para reutilizar servicios se usa composición.
Error común — guardar lo que se puede calcular: almacenar la multa como atributo. Si cambia el día o el plazo, el dato queda inconsistente.
Error común — confundir clase y objeto: la clase
Materialdescribe; cada material concreto (el libro «Fisica I») es un objeto. Los atributos de los objetos pertenecen a cada uno.
Error común — diseñar sin escenarios: un diseño que «se ve bien» pero nunca se ejecutó con los casos del enunciado suele esconder reglas olvidadas.
9. Verificación y reformulación
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
10. Libreta de trabajo
Reúna sus respuestas, sus predicciones y los resultados de las prácticas; revíselos y expórtelos como evidencia para el portafolio.
Este laboratorio interactivo requiere JavaScript habilitado en el navegador.
11. Preguntas de reflexión
- Elija un objeto o un proceso de su vida cotidiana (una cafetería, una agenda, un torneo deportivo). Indique tres modelos distintos de él, cada uno con un propósito diferente, y qué conserva cada uno.
- En el análisis de la biblioteca, estudiante y docente se modelaron como un atributo y no como subclases. Describa una regla nueva del problema que justificaría convertirlos en subclases.
- ¿Qué ocurre con el diseño si cambia la regla R3 (el límite pasa de tres a cinco)? ¿En cuántos lugares del código hay que cambiarlo y por qué conviene que sea uno solo?
- ¿Qué diferencia hay entre «la biblioteca tiene materiales» y «el libro es un material»? Dé un ejemplo propio de cada relación.
- Describa un caso en el que, al programar, descubrió (o descubriría) que una responsabilidad estaba mal asignada. ¿Qué indicios lo revelan?
12. Lecturas recomendadas
- Larman, C. (2004). Applying UML and patterns: An introduction to object-oriented analysis and design and iterative development (3rd ed.). Prentice Hall. (Identificación de clases conceptuales y asignación de responsabilidades; principio del experto en información.)
- Beck, K., & Cunningham, W. (1989). A laboratory for teaching object-oriented thinking. En OOPSLA '89 Conference Proceedings. ACM. (Origen de las fichas CRC.)
- Booch, G., Maksimchuk, R., Engle, M., Young, B., Conallen, J., & Houston, K. (2007). Object-oriented analysis and design with applications (3rd ed.). Addison-Wesley.
- Wirfs-Brock, R., & McKean, A. (2003). Object design: Roles, responsibilities, and collaborations. Addison-Wesley.
- Sebesta, R. (2015). Concepts of programming languages (11th ed.), capítulo sobre soporte para la programación orientada a objetos. Pearson.
- Python Software Foundation. The Python Tutorial, sección «Classes». Acceso libre en docs.python.org.