Café y Código

Módulo 1 de 4 — Vista Lógica

3.1 Diagramas de Clases

El diagrama de clases es el artefacto estrella de la Vista Lógica. Representa la estructura estática de un sistema orientado a objetos: qué elementos existen, qué información contienen, qué operaciones ejecutan y cómo colaboran entre sí.

📦
¿Qué objetos existen?

Las clases y entidades fundamentales que modelan el dominio del sistema.

📋
¿Qué datos contienen?

Atributos, tipos de datos y niveles de visibilidad (- privado, + público, # protegido).

¿Qué pueden hacer?

Constructores, métodos de negocio, getters/setters y firmas de retorno.

🔗
¿Cómo se relacionan?

Herencia (extends), contratos (implements), asociación (◇) y composición (◆).

💡 El plano universal: El diagrama UML es exactamente el mismo sin importar el lenguaje. En cada sección verás cómo se traduce directamente a Java, Python, C# y C++.

¿Qué es una clase?

Una clase es una representación o plantilla de un tipo de objeto dentro del sistema. En el modelado orientado a objetos y en UML, describe tres elementos fundamentales:

Usuario ← 1. Identidad (¿Qué es?)
- nombre: String
- email: String
← 2. Estado (¿Qué información tiene?)
+ login(): boolean
+ logout(): void
← 3. Comportamiento (¿Qué puede hacer?)
🏷️ 1. Identidad (Nombre)

El nombre de la clase permite identificar inequívocamente qué concepto del dominio representa en el sistema (ej. Usuario, Libro, Factura).

📊 2. Estado (Atributos)

Los atributos representan la información y valores que posee cada instancia (ej. nombre, email, saldo).

3. Comportamiento (Métodos)

Los métodos u operaciones representan las acciones y funciones que la clase puede ejecutar (ej. login(), logout(), calcularTotal()).

Fórmula fundamental: Clase = Identidad + Estado + Comportamiento

1. Descripción de la clase y los atributos

Un rectángulo UML con 3 compartimentos: nombre arriba, atributos al medio, métodos abajo. - privado, + público, # protegido.

UMLClase Libro — Atributos y Métodos
Libro
-titulo: StringString titulotitulotitulo
-autor: StringString autorautorautor
-paginas: intint paginaspaginaspaginas
-prestado: booleanboolean prestadoprestadoprestado
+getTitulo(): StringString getTitulo()getTitulo()getTitulo()
+prestar(): voidvoid prestar()prestar()prestar()
🔍 Clic para ampliar y hacer zoom
Libro
JAVA
1public class Libro { // PascalCase
2 // - privado → private
3 private String titulo;
4 private String autor;
5 private int paginas;
6 private boolean prestado;
7
8 // UML: - titulo: String → private String titulo;
9}
Regla UML:- titulo: String = privado String titulo. Si ves + es público. El tipo va después de :.

2. Constructor — con parámetro y sin parámetro

El constructor lleva el mismo nombre que la clase y no tiene tipo de retorno. Permite crear el objeto ya inicializado. Sobrecarga = dos constructores con distinta firma.

UMLSobrecarga de Constructores
Libro
-titulo: StringString titulotitulotitulo
-autor: StringString autorautorautor
+Libro()Libro()Libro()Libro()
+Libro(titulo: String, autor: String)Libro(String titulo, String autor)Libro(titulo, autor)Libro(titulo, autor)
+Libro(titulo: String, paginas: int)Libro(String titulo, int paginas)Libro(titulo, paginas)Libro(titulo, paginas)
🔍 Clic para ampliar y hacer zoom
Constructores
JAVA
1public class Libro {
2 private String titulo;
3 private String autor;
4
5 public Libro() { // sin parámetros
6 this.titulo = "Sin título";
7 this.autor = "Anónimo";
8 }
9 public Libro(String titulo, String autor) { // con parámetros
10 this.titulo = titulo;
11 this.autor = autor;
12 }
13 public Libro(String titulo, int paginas) { // sobrecarga distinta
14 this.titulo = titulo;
15 this.paginas = paginas;
16 }
17}
18Libro l1 = new Libro();
19Libro l2 = new Libro("Cien años", "García Márquez");
En UML el constructor se reconoce porque se llama igual que la clase y no muestra retorno. Si hay dos, es sobrecarga.

3. Setters and Getters — encapsulamiento

Atributos private + métodos getTitulo()/setTitulo() para controlar acceso y validación. En el diagrama: + getTitulo(): String y + setTitulo(t:String): void. Excepción Java: si el atributo es boolean, el getter se llama isPrestado() no getPrestado() (JavaBeans).

UMLEncapsulamiento — Getters y Setters
Libro
-titulo: StringString titulotitulotitulo
-paginas: intint paginaspaginaspaginas
-prestado: booleanboolean prestadoprestadoprestado
+getTitulo(): StringString getTitulo()getTitulo()getTitulo()
+setTitulo(t: String): voidvoid setTitulo(String t)setTitulo(t)setTitulo(t)
+getPaginas(): intint getPaginas()getPaginas()getPaginas()
+setPaginas(p: int): voidvoid setPaginas(int p)setPaginas(p)setPaginas(p)
+isPrestado(): booleanboolean isPrestado()isPrestado()isPrestado()
+setPrestado(p: boolean): voidvoid setPrestado(boolean p)setPrestado(p)setPrestado(p)
🔍 Clic para ampliar y hacer zoom
Getters y Setters
JAVA
1public class Libro {
2 private String titulo;
3 private int paginas;
4 private boolean prestado; // boolean → getter con 'is'
5
6 public String getTitulo() { // getter String/int → get
7 return titulo;
8 }
9 public void setTitulo(String titulo) { // setter con validación
10 if (titulo == null || titulo.isBlank()) throw new IllegalArgumentException();
11 this.titulo = titulo;
12 }
13 public int getPaginas() { return paginas; }
14 public void setPaginas(int paginas) {
15 if (paginas < 0) throw new IllegalArgumentException();
16 this.paginas = paginas;
17 }
18 // ¡Boolean usa 'is' en Java!
19 public boolean isPrestado() { // no getPrestado()
20 return prestado;
21 }
22 public void setPrestado(boolean prestado) {
23 this.prestado = prestado;
24 }
25}
☕ Java tip — boolean:isPrestado() no getPrestado(). private boolean prestado;isPrestado() / setPrestado(). Frameworks (Spring, Jackson) lo esperan así.

4. Métodos de clase personalizados

Más allá de getters/setters, la clase expone comportamiento: prestar(), devolver(), estaDisponible().

UMLMétodos Personalizados
Libro
-prestado: booleanboolean prestadoprestadoprestado
+prestar(): voidvoid prestar()prestar()prestar()
+devolver(): voidvoid devolver()devolver()devolver()
+estaDisponible(): booleanboolean estaDisponible()estaDisponible()estaDisponible()
+descripcion(): StringString descripcion()descripcion()descripcion()
🔍 Clic para ampliar y hacer zoom
Métodos Personalizados
JAVA
1public class Libro {
2 private boolean prestado;
3
4 public void prestar() { // camelCase
5 if (prestado) throw new IllegalStateException("Ya prestado");
6 prestado = true;
7 }
8 public void devolver() { prestado = false; }
9 public boolean estaDisponible() { return !prestado; }
10 public String descripcion() { return "Libro prestado=" + prestado; }
11}

5. Override — reescribir el comportamiento

Una subclase reescribe un método heredado para especializarlo. En UML se ve el mismo método en padre e hija; en código lleva anotación.

UMLOverride y Polimorfismo
Publicacion
-titulo: StringString titulotitulotitulo
+mostrar(): StringString mostrar()mostrar()mostrar()
extends (herencia)
Libro
-paginas: intint paginaspaginaspaginas
+mostrar(): StringString mostrar()mostrar()mostrar()
Revista
-numero: intint numeronumeronumero
+mostrar(): StringString mostrar()mostrar()mostrar()
EBook
-tamanoMb: doubledouble tamanoMbtamanoMbtamanoMb
+mostrar(): StringString mostrar()mostrar()mostrar()
🔍 Clic para ampliar y hacer zoom
Override y Herencia
JAVA
1public class Publicacion {
2 protected String titulo;
3 public Publicacion(String titulo) { this.titulo = titulo; }
4 public String mostrar() { return "Publicación: " + titulo; }
5}
6public class Libro extends Publicacion {
7 private int paginas;
8 public Libro(String titulo, int paginas) {
9 super(titulo); this.paginas = paginas;
10 }
11 @Override // obligatorio para override
12 public String mostrar() {
13 return "Libro: " + titulo + " (" + paginas + " pág.)";
14 }
15}
super() / super.mostrar() llama al padre. @Override en Java y override en C++/C# evitan errores si cambias la firma.

6. Conexiones — asociación y composición

Las clases rara vez viven solas. Una clase usa otra como atributo:

  • Asociación (◇—): una clase tiene otra pero no la posee. Libro tiene Etiqueta → si borro el libro, las etiquetas pueden seguir vivas.
  • Composición (◆—): una clase posee completamente a otra. Libro compone Autor → si borro el libro, su referencia al autor se destruye también.

En UML: diamante vacío (◇) = asociación débil, diamante lleno (◆) = composición fuerte.

UMLRelaciones — Asociación y Composición
Libro
-titulo: StringString titulotitulotitulo
+getAutor(): AutorAutor getAutor()getAutor()getAutor()
+addEtiqueta(e: Etiqueta): voidvoid addEtiqueta(Etiqueta e)addEtiqueta(e)addEtiqueta(e)
Composición (◆)
Autor
-nombre: StringString nombrenombrenombre
Asociación (◇)
Etiqueta
-nombre: StringString nombrenombrenombre
🔍 Clic para ampliar y hacer zoom
Asociación y Composición
JAVA
1import java.util.ArrayList;
2import java.util.List;
3
4public class Libro {
5 private String titulo;
6 private Autor autor; // composición: un autor por libro
7 private List<Etiqueta> etiquetas; // asociación: muchas etiquetas
8
9 public Libro(String titulo, Autor autor) {
10 this.titulo = titulo;
11 this.autor = autor;
12 this.etiquetas = new ArrayList<>();
13 }
14 public Autor getAutor() { return autor; }
15 public void addEtiqueta(Etiqueta e) { etiquetas.add(e); }
16}
17Libro l = new Libro("Rayuela", new Autor("Cortázar"));
18l.addEtiqueta(new Etiqueta("novela"))
En UML la línea con diamante va del contenedor al contenido. Diamante vacío (◇) = asociación (etiquetas sobreviven). Diamante lleno (◆) = composición (el contenido muere con el contenedor). El número junto a la línea indica cardinalidad: 1, 0..*, 1..*.

7. Herencia — class Libro extends Publicacion

La herencia permite reutilizar código: la subclase hereda atributos y métodos del padre y puede añadir los suyos o reescribirlos.

En UML: triángulo vacío (▽) apunta del hijo al padre. Relación"es-un": Libro es una Publicacion.

UMLJerarquía de Herencia — Publicación
Publicacion
-titulo: StringString titulotitulotitulo
-isbn: StringString isbnisbnisbn
+getTitulo(): StringString getTitulo()getTitulo()getTitulo()
+descripcion(): StringString descripcion()descripcion()descripcion()
extends (herencia)
Libro
-paginas: intint paginaspaginaspaginas
+getPaginas(): intint getPaginas()getPaginas()getPaginas()
+descripcion(): StringString descripcion()descripcion()descripcion()
Revista
-numero: intint numeronumeronumero
-frecuencia: StringString frecuenciafrecuenciafrecuencia
+getNumero(): intint getNumero()getNumero()getNumero()
+descripcion(): StringString descripcion()descripcion()descripcion()
EBook
-tamanoMb: doubledouble tamanoMbtamanoMbtamanoMb
+getTamanoMb(): doubledouble getTamanoMb()getTamanoMb()getTamanoMb()
+descripcion(): StringString descripcion()descripcion()descripcion()
🔍 Clic para ampliar y hacer zoom
Herencia
JAVA
1public class Publicacion {
2 protected String titulo;
3 protected String isbn;
4 public Publicacion(String titulo, String isbn) {
5 this.titulo = titulo;
6 this.isbn = isbn;
7 }
8 public String getTitulo() { return titulo; }
9 public String descripcion() { return "Publicación: " + titulo; }
10}
11
12public class Libro extends Publicacion {
13 private int paginas;
14 public Libro(String titulo, String isbn, int paginas) {
15 super(titulo, isbn);
16 this.paginas = paginas;
17 }
18 public int getPaginas() { return paginas; }
19 @Override
20 public String descripcion() {
21 return "Libro: " + titulo + " (" + paginas + " pág.)";
22 }
23}
24
25public class Revista extends Publicacion {
26 private int numero;
27 private String frecuencia;
28 public Revista(String titulo, String isbn, int numero, String frecuencia) {
29 super(titulo, isbn);
30 this.numero = numero;
31 this.frecuencia = frecuencia;
32 }
33 public int getNumero() { return numero; }
34 @Override
35 public String descripcion() {
36 return "Revista #" + numero + " (" + frecuencia + ")";
37 }
38}
Herencia vs Composición: Usa herencia cuando la relación es "es-un" (Libro es Publicación). Usa composición cuando es "tiene-un" (Libro tiene Autor). Preferir composición sobre herencia cuando sea posible.

8. Interfaces — implements Imprimible

Una interfaz es un contrato: define qué métodos debe tener una clase pero no cómo se implementan. Una clase puede implementar múltiples interfaces (a diferencia de heredar de un solo padre).

En UML: se escribe <<interface>> en la cabecera y la flecha es línea punteada con triángulo (▽LineStyle::dashed).

UMLInterfaces y Contratos
<<interface>>Imprimible
+imprimir(): StringString imprimir()imprimir()imprimir()
implements (interfaz)
Libro
-titulo: StringString titulotitulotitulo
+imprimir(): StringString imprimir()imprimir()imprimir()
Revista
-numero: intint numeronumeronumero
+imprimir(): StringString imprimir()imprimir()imprimir()
Recibo
-monto: doubledouble montomontomonto
+imprimir(): StringString imprimir()imprimir()imprimir()
🔍 Clic para ampliar y hacer zoom
Interfaces
JAVA
1public interface Imprimible {
2 String imprimir(); // todas las clases que implementen DEBEN tener imprimir()
3}
4
5public class Libro implements Imprimible {
6 private String titulo;
7 public Libro(String titulo) { this.titulo = titulo; }
8 @Override
9 public String imprimir() { return "Libro: " + titulo; }
10}
11
12public class Revista implements Imprimible {
13 private int numero;
14 public Revista(int numero) { this.numero = numero; }
15 @Override
16 public String imprimir() { return "Revista #" + numero; }
17}
18
19public class Recibo implements Imprimible {
20 private double monto;
21 public Recibo(double monto) { this.monto = monto; }
22 @Override
23 public String imprimir() { return "Recibo: $" + monto; }
24}
25
26// Polimorfismo
27Imprimible[] docs = { new Libro("Rayuela"), new Revista(42), new Recibo(2500) };
28for (Imprimible d : docs) {
29 System.out.println(d.imprimir());
30}
Herencia vs Interface: Herencia = "es-un" (un tipo). Interface = "puede-hacer" (un comportamiento). Java/C# permiten heredar de un solo padre pero implementarmúltiples interfaces. Ej: class Libro extends Publicacion implements Imprimible, Serializable.

Resumen — Los 4 pilares de las Clases

1. Clase
Atributos + métodos + constructores + getters/setters
2. Conexiones
Asociación (◇) y Composición (◆)
3. Herencia
extends — relación "es-un"
4. Interfaces
implements — contrato "puede-hacer"

Practica con los desafíos: Herencia e Interfaces.

🧱 Reto: Modela tu Libro completo

Crea una clase Libro con atributos titulo, autor, paginas, prestado, dos constructores (vacío y con titulo/autor), getters/setters con validación, métodos prestar()/devolver() y una subclase LibroDigital que haga override de mostrar().

👁️ Ver esquema UML esperado
UMLReto: Modelo Completo de Libro
Libro
-titulo: StringString titulotitulotitulo
-autor: StringString autorautorautor
-paginas: intint paginaspaginaspaginas
-prestado: booleanboolean prestadoprestadoprestado
+Libro()Libro()Libro()Libro()
+Libro(titulo: String, autor: String)Libro(String titulo, String autor)Libro(titulo, autor)Libro(titulo, autor)
+getTitulo(): StringString getTitulo()getTitulo()getTitulo()
+setTitulo(t: String): voidvoid setTitulo(String t)setTitulo(t)setTitulo(t)
+prestar(): voidvoid prestar()prestar()prestar()
+devolver(): voidvoid devolver()devolver()devolver()
+mostrar(): StringString mostrar()mostrar()mostrar()
extends (herencia)
LibroDigital
-tamanoMb: doubledouble tamanoMbtamanoMbtamanoMb
+mostrar(): StringString mostrar()mostrar()mostrar()
LibroFisico
-estante: StringString estanteestanteestante
+mostrar(): StringString mostrar()mostrar()mostrar()
Audiolibro
-duracionMin: intint duracionMinduracionMinduracionMin
+mostrar(): StringString mostrar()mostrar()mostrar()
🔍 Clic para ampliar y hacer zoom

Implementa en el lenguaje del selector y compara.

Ponte a prueba

Ko-fi
Donaciones
Apoyá cafeycodigo con un café en Ko-fi. Colaboradores: insignia, muro y zona exclusiva.