El patrón factory method define una interfaz para la creación de un objeto, pero dejando en manos de las subclases la decisión de qué clase concreta instanciar.
Permite que una clase delegue en sus subclases las instanciaciones.

El Producto declara la interfaz, que es común a todos los objetos que puede producir la clase creadora y sus subclases.
Los Productos Concretos son distintas implementaciones de la interfaz de producto.
La clase Creadora declara el método fábrica que devuelve nuevos objetos de producto. Es importante que el tipo de retorno de este método coincida con la interfaz de producto.
Los Creadores Concretos sobrescriben el Factory Method base, de modo que devuelva un tipo diferente de producto.
JuegoLaberintopublic class JuegoLaberinto {
JuegoLaberinto() {};
// factory methods:
Laberinto makeLaberinto() { return new Laberinto(); }
Sala makeSala(int numSala) { return new Sala(numSala); }
Pared makePared() { return new Pared(); }
Puerta makePuerta(Sala sala1, Sala sala2) {
return new Puerta(sala1, sala2);
}
Laberinto crearLaberinto () { ... }
}
Laberinto crearLaberinto () {
Laberinto miLab = makeLaberinto();
Sala hab1 = makeSala(1);
Sala hab2 = makeSala(2);
Puerta unaPuerta = makePuerta(hab1, hab2);
miLab.agregarSala(hab1);
miLab.agregarSala(hab2);
hab1.setLado(Direccion.NORTE, makePared());
hab1.setLado(Direccion.ESTE, unaPuerta);
hab1.setLado(Direccion.SUR, makePared());
hab1.setLado(Direccion.OESTE, makePared());
hab2.setLado(Direccion.NORTE, makePared());
hab2.setLado(Direccion.ESTE, makePared());
hab2.setLado(Direccion.SUR, makePared());
hab2.setLado(Direccion.OESTE, unaPuerta);
return miLab;
}
public class JuegoLaberintoMinado extends JuegoLaberinto {
Pared makePared() {
return new ParedMinada();
}
Sala makeSala(int numSala) {
return new SalaMinada(numSala);
}
}
public class JuegoLaberintoHechizado extends JuegoLaberinto {
Sala makeSala(int numSala) {
return new SalaHechizada(numSala, lanzarHechizo());
}
Puerta makePuerta(Sala sala1, Sala sala2) {
return new PuertaHechizada(sala1, sala2);
}
private Hechizo lanzarHechizo() { ... }
}


Ventajas:
Desventajas:





Class adapter:
Object adapter:


Ventajas:
Desventajas:


EnhancedWriter originalEnhancedWriter ampliado – herencia fuera de controlEnhancedWriter ampliado – herencia fuera de controlVentajas:
Desventajas:
El decorator cambia la piel, el strategy cambia las tripas


Sin distinguir entre Observable y Subject:
Publisher = Observable = SubjectConcreteSubscriber SubjectCon Subject separado:
Subscriber = ObserverObservable y SubjectObserver.update()
ConcreteObserver mantiene referencia a ConcreteSubjectConcreteSubject.getState() (pull)update(context) para pasar la información necesaria al suscriptorupdate(this) para que el suscriptor extraiga la información necesaria pidiéndosela al publicadorConcreteSubscriber.setPublisher() para vincularlos permanentemente (opción menos flexible)

Ventajas:
accept() depende del tipo de Visitor y del tipo de ElementDesventajas:
Element visitados deben ser estables

class Document {
var state: String = _
var expirationDate: Date = _
// ...
def publish(currentUser: User): Unit = state match {
case "draft" =>
if (currentUser.role == "admin") {
state = "published"
} else {
state = "moderation"
}
case "moderation" =>
if (currentUser.role == "admin") {
state = "published"
}
case "published" =>
if (new Date().after(expirationDate)) {
state = "draft"
}
case _ =>
// Handle any unexpected state.
throw new IllegalStateException(s"Invalid document state: $state")
}
}

El objeto de dominio mantiene el estado e incorpora la lógica de persistencia sobre ese estado.
save(), delete(), etc.)Desventajas:
El patrón Data Access Object se usa para abstraer y encapsular los accesos a las fuentes de datos, proporcionando una capa de persistencia con independencia del soporte concreto de almacenamiento (BD relacional, NoSQL, ficheros, etc.).
Problemas (sin DAO):
// Interfaz DAO - define el contrato
public interface UsuarioDAO {
void create(Usuario usuario);
Usuario read(int id);
void update(Usuario usuario);
void delete(int id);
List<Usuario> findAll();
}
// Implementación JDBC
public class UsuarioDAOImpl implements UsuarioDAO {
private Connection connection;
public UsuarioDAOImpl(Connection connection) {
this.connection = connection;
}
@Override
public void create(Usuario usuario) {
String sql =
"INSERT INTO usuarios (nombre, email) VALUES (?, ?)";
try (PreparedStatement stmt =
connection.prepareStatement(sql)) {
stmt.setString(1, usuario.getNombre());
stmt.setString(2, usuario.getEmail());
stmt.executeUpdate();
} catch (SQLException e) {
throw new PersistenceException(e);
}
}
...
...
@Override
public Usuario read(int id) {
String sql =
"SELECT * FROM usuarios WHERE id = ?";
try (PreparedStatement stmt =
connection.prepareStatement(sql)) {
stmt.setInt(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return new Usuario(
rs.getInt("id"),
rs.getString("nombre"),
rs.getString("email")
);
}
}
} catch (SQLException e) {
throw new PersistenceException(e);
}
return null;
}
// ... otros métodos (update, delete, findAll)
}
Ventajas:
Desventajas:
Evolución moderna del DAO que surge con la popularidad de frameworks como Spring Data JPA. Ofrece una abstracción de más alto nivel que el DAO tradicional, proporcionando operaciones CRUD genéricas sin necesidad de implementación manual.
Relación Repository
// Interfaz Repository - hereda CRUD automático
@Repository
public interface UsuarioRepository extends JpaRepository<Usuario, Long> {
// Métodos derivados (queries generadas automáticamente)
Optional<Usuario> findByEmail(String email);
List<Usuario> findByNombreContainingIgnoreCase(String nombre);
List<Usuario> findByCreatedAtAfter(LocalDateTime fecha);
boolean existsByEmail(String email);
}
// Entity con JPA
@Entity
@Table(name = "usuarios")
@Data
@NoArgsConstructor
public class Usuario {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nombre;
@Column(unique = true, nullable = false)
private String email;
@CreationTimestamp
private LocalDateTime createdAt;
}
// Usando el Repository en un Servicio
@Service
public class UsuarioService {
private final UsuarioRepository usuarioRepository;
@Autowired
public UsuarioService(UsuarioRepository usuarioRepository) {
this.usuarioRepository = usuarioRepository;
}
public Usuario crearUsuario(Usuario usuario) {
if (usuarioRepository.existsByEmail(usuario.getEmail())) {
throw new EmailYaExisteException();
}
return usuarioRepository.save(usuario);
}
public Usuario obtenerPorId(Long id) {
return usuarioRepository.findById(id)
.orElseThrow(() -> new UsuarioNoEncontradoException());
}
public List<Usuario> buscarPorNombre(String nombre) {
return usuarioRepository.
findByNombreContainingIgnoreCase(nombre);
}
}
CrudRepository@Transactional por defecto@DataJpaTestSirve para crear objetos planos o Plain Old Java Objects (POJO) que se envían entre aplicaciones, capas, o servidores remotos. Un DTO
Problema a resolver:
// Entity JPA (contiene datos + lógica de negocio)
@Entity
@Table(name = "usuarios")
@Data
@NoArgsConstructor
public class Usuario {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nombre;
@Column(unique = true, nullable = false)
private String email;
@JsonIgnore // No serializar en JSON
private String passwordHash;
@CreationTimestamp
private LocalDateTime createdAt;
// Métodos de negocio
public boolean validarPassword(String rawPassword) {
return BCrypt.checkpw(rawPassword, this.passwordHash);
}
}
// DTO - solo datos públicos (sin contraseña)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UsuarioDTO {
private Long id;
private String nombre;
private String email;
private LocalDateTime createdAt;
}
// Mapper usando ModelMapper (spring-boot-starter-modelmapper)
@Component
public class UsuarioMapper {
private final ModelMapper modelMapper;
@Autowired
public UsuarioMapper(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
public UsuarioDTO toDTO(Usuario usuario) {
return modelMapper.map(usuario, UsuarioDTO.class);
}
public Usuario toEntity(UsuarioDTO usuarioDTO) {
return modelMapper.map(usuarioDTO, Usuario.class);
}
public List<UsuarioDTO> toDTOList(List<Usuario> usuarios) {
return usuarios.stream()
.map(this::toDTO)
.collect(Collectors.toList());
}
}
// Controlador REST usando DTO
@RestController
@RequestMapping("/api/usuarios")
public class UsuarioController {
private final UsuarioService usuarioService;
private final UsuarioMapper usuarioMapper;
@GetMapping("/{id}")
public ResponseEntity<UsuarioDTO>
obtenerUsuario(@PathVariable Long id) {
Usuario usuario = usuarioService.obtenerPorId(id);
return ResponseEntity.ok(usuarioMapper.toDTO(usuario));
}
@GetMapping
public ResponseEntity<List<UsuarioDTO>>
listarUsuarios() {
List<Usuario> usuarios = usuarioService.listarTodos();
return ResponseEntity.ok(usuarioMapper.toDTOList(usuarios));
}
@PostMapping
public ResponseEntity<UsuarioDTO>
crearUsuario(@RequestBody UsuarioDTO usuarioDTO) {
Usuario usuario = usuarioMapper.toEntity(usuarioDTO);
Usuario creado = usuarioService.crearUsuario(usuario);
return ResponseEntity.created(URI.create("/api/usuarios/" +
creado.getId()))
.body(usuarioMapper.toDTO(creado));
}
}
Ventajas:
Desventajas:
@Service
public class UsuarioService {
private final UsuarioRepository usuarioRepository;
private final UsuarioMapper usuarioMapper;
@Autowired
public UsuarioService(UsuarioRepository usuarioRepository,
UsuarioMapper usuarioMapper) {
this.usuarioRepository = usuarioRepository;
this.usuarioMapper = usuarioMapper;
}
public UsuarioDTO obtenerPorId(Long id) {
Usuario usuario = usuarioRepository.findById(id)
.orElseThrow(() -> new UsuarioNoEncontradoException());
return usuarioMapper.toDTO(usuario); // Entity → DTO
}
public List<UsuarioDTO> buscarPorNombre(String nombre) {
List<Usuario> usuarios = usuarioRepository
.findByNombreContainingIgnoreCase(nombre);
return usuarioMapper.toDTOList(usuarios); // List<Entity> → List<DTO>
}
public UsuarioDTO crearUsuario(UsuarioDTO usuarioDTO) {
Usuario usuario = usuarioMapper.toEntity(usuarioDTO); // DTO → Entity
Usuario creado = usuarioRepository.save(usuario);
return usuarioMapper.toDTO(creado); // Entity → DTO
}
}
- Los patrones de diseño surgen a partir del libro *A Pattern Language: Towns, Buildings, Construction* de Cristopher Alexander. - La inspiración del libro fueron las ciudades medievales, atractivas y armoniosas, que fueron construidas según regulaciones locales que requerían ciertas características, pero que permitían al arquitecto adaptarlas a situaciones particulares. - En el libro se suministran reglas e imágenes y se describen métodos exactos para construir diseños prácticos, seguros y atractivos a cualquier escala. También recopila modelos anteriores (con ventajas/desventajas) con el fin de usarlos en un futuro. - El libro recomienda que las decisiones sobre la construcción de edificios se tomen de acuerdo al entorno preciso de cada proyecto.
@startuml top to bottom direction scale 700 width scale 600 height class Product class Creator class ConcreteProduct class ConcreteCreator Creator : factoryMethod() Creator : anOperation() ConcreteCreator : factoryMethod() Creator <|–down- ConcreteCreator Product <|–down- ConcreteProduct ConcreteProduct <- ConcreteCreator hide members show methods note top of Creator The Creator is a class that contains the implementation for all of the methods to manipulate products, except for the factory method. end note note right of Creator The abstract factoryMethod() is what all Creator subclasses must implement. end note note right of ConcreteCreator The ConcreteCreator implements the factoryMethod(), which is the method that actually produces products. end note note “The ConcreteCreator is responsible for\ncreating one or more concrete products. It\nis the only class that has the knowledge of\nhow to create these products.” as n1 ConcreteProduct .. n1 ConcreteCreator .. n1 note “All products must implement\nthe same interface so that the\nclasses which use the products\ncan refer to the interface,\nnot the concrete class.” as n2 n2 . ConcreteProduct n2 . Product @enduml ---
@startuml class Client class Invoker class Command <<interface>> class Receiver class ConcreteCommand Invoker : setCommand() Command : execute() Command : undo() Receiver : action() ConcreteCommand : execute() ConcreteCommand : undo() Client -> Receiver Client -> ConcreteCommand Receiver <- ConcreteCommand Invoker -> Command Command <|.. ConcreteCommand note left of Client The Client is responsible for creating a ConcreteCommand and setting its Receiver. end note note bottom of Receiver The Receiver knows how to perform the work needed to carry out the request. Any class can act as a Receiver. end note note bottom of ConcreteCommand The ConcreteCommand defines a binding between an action and a Receiver. The Invoker makes a request by calling execute() and the ConcreteCommand carries it out by calling one or more actions on the Receiver. end note note left of Invoker The Invoker holds a command and at some point asks the command to carry out a request by calling its execute() method. end note note top of Command Command declares an interface for all commands. A command is invoked through its execute() method, which asks a receiver to perform its action. end note note right of ConcreteCommand::execute() The execute method invokes the action(s) on the receiver needed to fulfill the request; public void execute() { receiver.action() } end note @enduml
@startuml class Client class Target <<interface>> class Adapter class Adaptee Target : request() Adapter : request() Adaptee : specificRequest() Client -> Target Target <|.. Adapter Adapter -> Adaptee note on link Adapter is composed with the Adapter. end note note bottom of Client The client sees only the Target interface end note note “The Adapter implements\nthe Target interface.” as n1 Target .. n1 n1 .. Adapter note bottom of Adaptee All requests get delegated to the Adaptee. end note @enduml
@startuml class Client class Component class Leaf class Composite Component : operation() Component : add(Component) Component : remove(Component) Component : getChild(int) Leaf : operation() Composite : operation() Composite : add(Component) Composite : remove(Component) Composite : getChild(int) Client -> Component Component <|– Leaf Component <|– Composite Component “0..*” <–o “1” Composite note top of Client The Client uses the Component interface to manipulate the objects in the composition. end note note top of Component The Component defines an interface for all objects in the composition: both the composite and the leaf nodes. end note note top of Component The Component may implement a default behavior for add(), remove(), getChild() and its operations. end note note bottom of Leaf A Leaf has no children. end note note left of Leaf Note that the Leaf also inherits methods like add(), remove() and getChild(), which do not necessarily make a lot of sense for a leaf node. We are going to come back to this issue. end note note bottom of Leaf A Leaf defines the behavior for the elements in the composition. It does this by implementing the operations the Composite supports. end note note bottom of Composite The Composite’s role is to define behavior of the components having children and to store child components. end note note right of Composite The Composite also implements the Leaf- related operations. Note that some of these may not make sense on a Composite, so in that case an exception might be generated. end note @enduml
@startuml skinparam componentStyle uml2 class Component class ConcreteComponent class Decorator class ConcreteDecoratorA class ConcreteDecoratorB Component : methodA() Component : methodB() Component : // otherMethods() ConcreteComponent : methodA() ConcreteComponent : methodB() ConcreteComponent : // otherMethods() Decorator : methodA() Decorator : methodB() Decorator : // otherMethods() ConcreteDecoratorA : Component wrappedObject ConcreteDecoratorA : methodA() ConcreteDecoratorA : methodB() ConcreteDecoratorA : newBehavior() ConcreteDecoratorA : // otherMethods() ConcreteDecoratorB : Component wrappedObject ConcreteDecoratorB : Object newState ConcreteDecoratorB : methodA() ConcreteDecoratorB : methodB() ConcreteDecoratorB : // otherMethods() Component <|– ConcreteComponent Component <|– Decorator Decorator <|– ConcreteDecoratorA Decorator <|– ConcreteDecoratorB Decorator –> Component : component note right on link Each component can be used on its own, or wrapped by a decorator component end note note bottom of ConcreteComponent The ConreteComponent is the object we are going to dynamically add new behavior to it. It extends Component. end note note bottom of Decorator Decorators implement the same interface or abstract class as the component they are going to decorate. end note note bottom of ConcreteDecoratorB Decorators can extend the state of the component end note note bottom of ConcreteDecoratorB Decorators can add new methods; however, new behavior is typically added by doing computation before or after an existing method in the component. end note note bottom of ConcreteDecoratorA The ConcreteDecorator has an instance variable for the thing it decorates (the Component the Decorator wraps). end note @enduml
@startuml class Client class Invoker class Command <<interface>> class Receiver class ConcreteCommand Invoker : setCommand() Command : execute() Command : undo() Receiver : action() ConcreteCommand : execute() ConcreteCommand : undo() Client -> Receiver Client -> ConcreteCommand Receiver <- ConcreteCommand Invoker -> Command Command <|.. ConcreteCommand note left of Client The Client is responsible for creating a ConcreteCommand and setting its Receiver. end note note bottom of Receiver The Receiver knows how to perform the work needed to carry out the request. Any class can act as a Receiver. end note note bottom of ConcreteCommand The ConcreteCommand defines a binding between an action and a Receiver. The Invoker makes a request by calling execute() and the ConcreteCommand carries it out by calling one or more actions on the Receiver. end note note left of Invoker The Invoker holds a command and at some point asks the command to carry out a request by calling its execute() method. end note note top of Command Command declares an interface for all commands. A command is invoked through its execute() method, which asks a receiver to perform its action. end note note right of ConcreteCommand::execute() The execute method invokes the action(s) on the receiver needed to fulfill the request; public void execute() { receiver.action() } end note @enduml