* Typo corrections * bridge pattern translated to spanish * builder pattern translated to spanish * context-object pattern translated to spanish * converter pattern translated to spanish * dependency injection pattern translated to spanish * factory pattern translated to spanish * factory-kit pattern translated to spanish * factory-method pattern translated to spanish * monostate pattern translated to spanish * multiton pattern translated to spanish * object mother pattern translated to spanish * object pool pattern translated to spanish * property pattern translated to spanish * prototype pattern translated to spanish * registry pattern translated to spanish * step builder pattern translated to spanish * value object pattern translated to spanish * typo corrections and resources urls refactor * grammar corrections * Add image to each pattern * translate all structural patterns to Spanish --------- Co-authored-by: luismateoh <luismateohm@gmail.com> Co-authored-by: luis.hincapie <luis.hincapie@blankfactor.com>
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Business Delegate
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
|
||||
El patrón Business Delegate añade una capa de abstracción entre los niveles de presentación y de negocio. Al utilizar
|
||||
este patrón, conseguimos un acoplamiento flexible entre los niveles y encapsulamos el conocimiento sobre cómo localizar,
|
||||
conectar e interactuar con los objetos de negocio que componen la aplicación.
|
||||
|
||||
## También conocido como
|
||||
|
||||
Service Representative
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo del mundo real
|
||||
|
||||
> Una aplicación para teléfonos móviles promete transmitir a tu dispositivo cualquier película existente. Captura la
|
||||
> cadena de búsqueda del usuario y se la pasa al Delegado de Negocio. El Delegado de Negocio selecciona el
|
||||
> servicio de streaming de vídeo más adecuado y reproduce el vídeo.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> Business Delegate añade una capa de abstracción entre los niveles de presentación y de negocio.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> Business Delegate es un patrón de diseño de Java EE. Este patrón está dirigido a reducir el acoplamiento entre los
|
||||
> servicios de negocio y el nivel de presentación conectado, y para ocultar los detalles de implementación de los
|
||||
> servicios (incluyendo la búsqueda y la accesibilidad de la arquitectura EJB). Los delegados de negocio actúan como un
|
||||
> adaptador para invocar objetos de negocio desde la capa de presentación.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
En primer lugar, tenemos una abstracción para los servicios de streaming de vídeo `VideoStreamingService` y un par de
|
||||
implementaciones `NetflixService` y `YouTubeService`.
|
||||
|
||||
```java
|
||||
public interface VideoStreamingService {
|
||||
void doProcessing();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class NetflixService implements VideoStreamingService {
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("NetflixService is now processing");
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class YouTubeService implements VideoStreamingService {
|
||||
@Override
|
||||
public void doProcessing() {
|
||||
LOGGER.info("YouTubeService is now processing");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A continuación, tenemos un servicio de búsqueda `BusinessLookup` que decide qué servicio de transmisión de vídeo
|
||||
utilizar.
|
||||
|
||||
```java
|
||||
|
||||
@Setter
|
||||
public class BusinessLookup {
|
||||
|
||||
private NetflixService netflixService;
|
||||
private YouTubeService youTubeService;
|
||||
|
||||
public VideoStreamingService getBusinessService(String movie) {
|
||||
if (movie.toLowerCase(Locale.ROOT).contains("die hard")) {
|
||||
return netflixService;
|
||||
} else {
|
||||
return youTubeService;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
El Delegado de Negocio `BusinessDelegate` utiliza una búsqueda de negocio para dirigir las solicitudes de reproducción
|
||||
de películas a un servicio de streaming de vídeo adecuado.
|
||||
|
||||
```java
|
||||
|
||||
@Setter
|
||||
public class BusinessDelegate {
|
||||
|
||||
private BusinessLookup lookupService;
|
||||
|
||||
public void playbackMovie(String movie) {
|
||||
VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie);
|
||||
videoStreamingService.doProcessing();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
El cliente móvil `MobileClient` utiliza Business Delegate para llamar al nivel de negocio.
|
||||
|
||||
```java
|
||||
public class MobileClient {
|
||||
|
||||
private final BusinessDelegate businessDelegate;
|
||||
|
||||
public MobileClient(BusinessDelegate businessDelegate) {
|
||||
this.businessDelegate = businessDelegate;
|
||||
}
|
||||
|
||||
public void playbackMovie(String movie) {
|
||||
businessDelegate.playbackMovie(movie);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Por último, podemos demostrar el ejemplo completo en acción.
|
||||
|
||||
```java
|
||||
public static void main(String[]args){
|
||||
|
||||
// preparar los objetos
|
||||
var businessDelegate=new BusinessDelegate();
|
||||
var businessLookup=new BusinessLookup();
|
||||
businessLookup.setNetflixService(new NetflixService());
|
||||
businessLookup.setYouTubeService(new YouTubeService());
|
||||
businessDelegate.setLookupService(businessLookup);
|
||||
|
||||
// crear el cliente y utilizar el Business Delegate
|
||||
var client=new MobileClient(businessDelegate);
|
||||
client.playbackMovie("Die Hard 2");
|
||||
client.playbackMovie("Maradona: The Greatest Ever");
|
||||
}
|
||||
```
|
||||
|
||||
Aquí está la salida de la consola.
|
||||
|
||||
```
|
||||
21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing
|
||||
21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Patrón de localización de servicios](https://java-design-patterns.com/patterns/service-locator/)
|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Business Delegate cuando
|
||||
|
||||
* Desea un acoplamiento flexible entre los niveles de presentación y de negocio.
|
||||
* Quieres orquestar llamadas a múltiples servicios de negocio
|
||||
* Se desea encapsular las búsquedas y llamadas a servicios.
|
||||
* Es necesario abstraer y encapsular la comunicación entre la capa cliente y los servicios de negocio.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Patrón Delegado de Negocio en TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm)
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* Aplicaciones empresariales que utilicen Java EE (Java Platform, Enterprise Edition)
|
||||
* Aplicaciones que requieren acceso remoto a servicios empresariales
|
||||
|
||||
## Consecuencias
|
||||
|
||||
Ventajas:
|
||||
|
||||
* Desacoplamiento de los niveles de presentación y de negocio: Permite que el nivel de cliente y los servicios
|
||||
empresariales evolucionen de forma independiente.
|
||||
* Transparencia de ubicación: Los clientes no se ven afectados por cambios en la ubicación o la instanciación de los
|
||||
servicios de negocio.
|
||||
* Reutilización y escalabilidad: Los objetos Business Delegate pueden ser reutilizados por múltiples clientes, y el
|
||||
patrón soporta el equilibrio de carga y la escalabilidad.
|
||||
carga y escalabilidad.
|
||||
|
||||
Contrapartidas:
|
||||
|
||||
* Complejidad: Introduce capas y abstracciones adicionales que pueden aumentar la complejidad.
|
||||
* Sobrecarga de rendimiento: La indirección adicional puede suponer una ligera penalización en el rendimiento.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Localizador de servicios](https://java-design-patterns.com/patterns/service-locator/): El Delegado de Negocio (
|
||||
Business Delegate) utiliza el Localizador de Servicios (Service Locator) para localizar servicios de negocio.
|
||||
* [Fachada de Sesión](https://java-design-patterns.com/patterns/session-facade/): El Delegado de Negocio (Business
|
||||
Delegate) puede utilizar la Fachada de Sesión (Session Facade) para proporcionar una interfaz unificada a un conjunto
|
||||
de servicios de negocio.
|
||||
* [Entidad Compuesta](https://java-design-patterns.com/patterns/composite-entity/): El Delegado (Business Delegate) de
|
||||
Negocio puede utilizar Entidad Compuesta (Composite Entity) para gestionar el estado de los servicios de negocio.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
|
||||
* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947)
|
||||
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: Composite Entity
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Client-server
|
||||
- Data access
|
||||
- Enterprise patterns
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
* Coarse-Grained Entity
|
||||
|
||||
## Propósito
|
||||
|
||||
El patrón de diseño Entidad Compuesta tiene como objetivo gestionar un conjunto de objetos persistentes
|
||||
interrelacionados como si fueran una única entidad. Se utiliza comúnmente en el contexto de Enterprise JavaBeans (EJB) y
|
||||
marcos empresariales similares para representar estructuras de datos basadas en gráficos dentro de modelos de negocio,
|
||||
permitiendo a los clientes tratarlos como una sola unidad.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> En una consola, puede haber muchas interfaces que necesiten ser gestionadas y controladas. Usando el patrón de entidad
|
||||
> compuesta, objetos dependientes como mensajes y señales pueden ser combinados y controlados usando un único objeto.
|
||||
|
||||
En palabras llanas
|
||||
|
||||
> El patrón de entidad compuesta permite representar y gestionar un conjunto de objetos relacionados mediante un objeto
|
||||
> unificado.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Necesitamos una solución genérica para el problema. Para ello, vamos a introducir un patrón genérico de entidad
|
||||
compuesta.
|
||||
|
||||
```java
|
||||
public abstract class DependentObject<T> {
|
||||
|
||||
T data;
|
||||
|
||||
public void setData(T message) {
|
||||
this.data = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CoarseGrainedObject<T> {
|
||||
|
||||
DependentObject<T>[] dependentObjects;
|
||||
|
||||
public void setData(T... data) {
|
||||
IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i]));
|
||||
}
|
||||
|
||||
public T[] getData() {
|
||||
return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
La entidad compuesta especializada `consola` hereda de esta clase base de la siguiente manera.
|
||||
|
||||
```java
|
||||
public class MessageDependentObject extends DependentObject<String> {
|
||||
|
||||
}
|
||||
|
||||
public class SignalDependentObject extends DependentObject<String> {
|
||||
|
||||
}
|
||||
|
||||
public class ConsoleCoarseGrainedObject extends CoarseGrainedObject<String> {
|
||||
|
||||
@Override
|
||||
public String[] getData() {
|
||||
super.getData();
|
||||
return new String[] {
|
||||
dependentObjects[0].getData(), dependentObjects[1].getData()
|
||||
};
|
||||
}
|
||||
|
||||
public void init() {
|
||||
dependentObjects = new DependentObject[] {
|
||||
new MessageDependentObject(), new SignalDependentObject()};
|
||||
}
|
||||
}
|
||||
|
||||
public class CompositeEntity {
|
||||
|
||||
private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject();
|
||||
|
||||
public void setData(String message, String signal) {
|
||||
console.setData(message, signal);
|
||||
}
|
||||
|
||||
public String[] getData() {
|
||||
return console.getData();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Gestionando ahora la asignación de objetos mensaje y señal con la entidad compuesta `consola`.
|
||||
|
||||
```java
|
||||
var console=new CompositeEntity();
|
||||
console.init();
|
||||
console.setData("No Danger","Green Light");
|
||||
Arrays.stream(console.getData()).forEach(LOGGER::info);
|
||||
console.setData("Danger","Red Light");
|
||||
Arrays.stream(console.getData()).forEach(LOGGER::info);
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
* Útil en aplicaciones empresariales donde los objetos de negocio son complejos e involucran varios objetos
|
||||
interdependientes.
|
||||
* Ideal para escenarios donde los clientes necesitan trabajar con una interfaz unificada para un conjunto de objetos en
|
||||
lugar de entidades individuales.
|
||||
* Aplicable en sistemas que requieren una vista simplificada de un modelo de datos complejo para clientes o servicios
|
||||
externos.
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* Aplicaciones empresariales con modelos de negocio complejos, particularmente aquellas que utilizan EJB o marcos
|
||||
empresariales similares.
|
||||
* Sistemas que requieren abstracción sobre esquemas de bases de datos complejos para simplificar las interacciones con
|
||||
los clientes.
|
||||
* Aplicaciones que necesitan reforzar la consistencia o las transacciones a través de múltiples objetos en una entidad
|
||||
de negocio.
|
||||
|
||||
## Consecuencias
|
||||
|
||||
Ventajas:
|
||||
|
||||
* Simplifica las interacciones del cliente con modelos de entidad complejos proporcionando una interfaz unificada.
|
||||
* Mejora la reutilización y el mantenimiento de la capa de negocio al desacoplar el código del cliente de los complejos
|
||||
componentes internos de las entidades de negocio.
|
||||
* Facilita la gestión de transacciones y la aplicación de la coherencia en un conjunto de objetos relacionados.
|
||||
|
||||
Contrapartidas:
|
||||
|
||||
* Puede introducir un nivel de indirección que podría afectar al rendimiento.
|
||||
* Puede dar lugar a interfaces de grano demasiado grueso que podrían no ser tan flexibles para todas las necesidades de
|
||||
los clientes.
|
||||
* Requiere un diseño cuidadoso para evitar entidades compuestas hinchadas que sean difíciles de gestionar.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Decorador](https://java-design-patterns.com/patterns/decorator/): Para añadir dinámicamente comportamiento a objetos
|
||||
individuales dentro de la entidad compuesta sin afectar a la estructura.
|
||||
* [Fachada](https://java-design-patterns.com/patterns/facade/): Proporciona una interfaz simplificada a un subsistema
|
||||
complejo, de forma similar a como una entidad compuesta simplifica el acceso a un conjunto de objetos.
|
||||
* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Útil para gestionar objetos compartidos dentro de
|
||||
una entidad compuesta para reducir la huella de memoria.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern)
|
||||
* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap)
|
||||
* [Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML](https://amzn.to/49mslqS)
|
||||
* [Patterns of Enterprise Application Architecture](https://amzn.to/3xjKdpe)
|
||||
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,385 @@
|
||||
---
|
||||
title: Composite View
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Enterprise patterns
|
||||
- Presentation
|
||||
---
|
||||
|
||||
## Propósito
|
||||
|
||||
El objetivo principal del patrón de diseño Composite View es componer objetos en estructuras de árbol para representar
|
||||
jerarquías parte-todo. Esto permite a los clientes tratar objetos individuales y composiciones de objetos de manera
|
||||
uniforme, simplificando la gestión de estructuras complejas.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo del mundo real
|
||||
|
||||
> Un sitio de noticias quiere mostrar la fecha actual y las noticias a diferentes usuarios basándose en las preferencias
|
||||
> de ese usuario. El sitio de noticias sustituirá en diferentes componentes de alimentación de noticias en función de
|
||||
> los intereses del usuario, por defecto a las noticias locales.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón de vista compuesta consiste en tener una vista principal compuesta por subvistas más pequeñas. El diseño de
|
||||
> esta vista compuesta se basa en una plantilla. Un View-manager decide entonces qué subvistas incluir en esta
|
||||
> plantilla.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> Vistas compuestas que están formadas por múltiples subvistas atómicas. Cada componente de la plantilla puede incluirse
|
||||
> dinámicamente en el conjunto y el diseño de la página puede gestionarse independientemente del contenido. Esta
|
||||
> solución permite crear una vista compuesta basada en la inclusión y sustitución de fragmentos modulares de plantillas
|
||||
> dinámicas y estáticas. Promueve la reutilización de porciones atómicas de la vista fomentando el diseño modular.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Dado que se trata de un patrón de desarrollo web, se requiere un servidor para demostrarlo. Este ejemplo utiliza Tomcat
|
||||
10.0.13 para ejecutar el servlet, y este ejemplo programático sólo funcionará con Tomcat 10+.
|
||||
|
||||
En primer lugar, existe `AppServlet` que es un `HttpServlet` que se ejecuta en Tomcat 10+.
|
||||
|
||||
```java
|
||||
public class AppServlet extends HttpServlet {
|
||||
private String msgPartOne = "<h1>This Server Doesn't Support";
|
||||
private String msgPartTwo = "Requests</h1>\n"
|
||||
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
|
||||
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
|
||||
|
||||
private String destination = "newsDisplay.jsp";
|
||||
|
||||
public AppServlet() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
|
||||
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
|
||||
req.setAttribute("properties", reqParams);
|
||||
requestDispatcher.forward(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
resp.setContentType("text/html");
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.println(msgPartOne + " Post " + msgPartTwo);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
resp.setContentType("text/html");
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.println(msgPartOne + " Delete " + msgPartTwo);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPut(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
resp.setContentType("text/html");
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.println(msgPartOne + " Put " + msgPartTwo);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Este servlet no forma parte del patrón, y simplemente reenvía las peticiones GET a la JSP correcta. Las peticiones PUT,
|
||||
POST y DELETE no están soportadas y simplemente mostrarán un mensaje de error.
|
||||
|
||||
La gestión de la vista en este ejemplo se realiza a través de una clase javabean: `ClientPropertiesBean`, que almacena
|
||||
las preferencias del usuario.
|
||||
|
||||
```java
|
||||
public class ClientPropertiesBean implements Serializable {
|
||||
|
||||
private static final String WORLD_PARAM = "world";
|
||||
private static final String SCIENCE_PARAM = "sci";
|
||||
private static final String SPORTS_PARAM = "sport";
|
||||
private static final String BUSINESS_PARAM = "bus";
|
||||
private static final String NAME_PARAM = "name";
|
||||
|
||||
private static final String DEFAULT_NAME = "DEFAULT_NAME";
|
||||
private boolean worldNewsInterest;
|
||||
private boolean sportsInterest;
|
||||
private boolean businessInterest;
|
||||
private boolean scienceNewsInterest;
|
||||
private String name;
|
||||
|
||||
public ClientPropertiesBean() {
|
||||
worldNewsInterest = true;
|
||||
sportsInterest = true;
|
||||
businessInterest = true;
|
||||
scienceNewsInterest = true;
|
||||
name = DEFAULT_NAME;
|
||||
|
||||
}
|
||||
|
||||
public ClientPropertiesBean(HttpServletRequest req) {
|
||||
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
|
||||
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
|
||||
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
|
||||
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
|
||||
String tempName = req.getParameter(NAME_PARAM);
|
||||
if (tempName == null || tempName == "") {
|
||||
tempName = DEFAULT_NAME;
|
||||
}
|
||||
name = tempName;
|
||||
}
|
||||
// getters and setters generated by Lombok
|
||||
}
|
||||
```
|
||||
|
||||
Este javabean tiene un constructor por defecto, y otro que toma un `HttpServletRequest`.
|
||||
|
||||
Este segundo constructor toma el objeto de solicitud, analiza los parámetros de la solicitud que contienen las
|
||||
preferencias del usuario para los diferentes tipos de noticias.
|
||||
|
||||
La plantilla para la página de noticias está en `newsDisplay.jsp`.
|
||||
|
||||
```html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.centerTable {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
tr {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
|
||||
<h1>Welcome <%= propertiesBean.getName()%></h1>
|
||||
<jsp:include page="header.jsp"></jsp:include>
|
||||
<table class="centerTable">
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
<% if(propertiesBean.isWorldNewsInterest()) { %>
|
||||
<td><%@include file="worldNews.jsp"%></td>
|
||||
<% } else { %>
|
||||
<td><%@include file="localNews.jsp"%></td>
|
||||
<% } %>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<% if(propertiesBean.isBusinessInterest()) { %>
|
||||
<td><%@include file="businessNews.jsp"%></td>
|
||||
<% } else { %>
|
||||
<td><%@include file="localNews.jsp"%></td>
|
||||
<% } %>
|
||||
<td></td>
|
||||
<% if(propertiesBean.isSportsInterest()) { %>
|
||||
<td><%@include file="sportsNews.jsp"%></td>
|
||||
<% } else { %>
|
||||
<td><%@include file="localNews.jsp"%></td>
|
||||
<% } %>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<% if(propertiesBean.isScienceNewsInterest()) { %>
|
||||
<td><%@include file="scienceNews.jsp"%></td>
|
||||
<% } else { %>
|
||||
<td><%@include file="localNews.jsp"%></td>
|
||||
<% } %>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Esta página JSP es la plantilla. Declara una tabla con tres filas, con un componente en la primera fila, dos componentes
|
||||
en la segunda fila y un componente en la tercera fila.
|
||||
|
||||
Los scriplets en el archivo son parte de la estrategia de gestión de vistas que incluyen diferentes subvistas atómicas
|
||||
basadas en las preferencias del usuario en el Javabean.
|
||||
|
||||
A continuación se muestran dos ejemplos de las subvistas atómicas simuladas utilizadas en el
|
||||
compuesto: `businessNews.jsp`
|
||||
|
||||
```html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
tr {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>
|
||||
Generic Business News
|
||||
</h2>
|
||||
<table style="margin-right: auto; margin-left: auto">
|
||||
<tr>
|
||||
<td>Stock prices up across the world</td>
|
||||
<td>New tech companies to invest in</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Industry leaders unveil new project</td>
|
||||
<td>Price fluctuations and what they mean</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
`localNews.jsp`
|
||||
|
||||
```html
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<div style="text-align: center">
|
||||
<h3>
|
||||
Generic Local News
|
||||
</h3>
|
||||
<ul style="list-style-type: none">
|
||||
<li>
|
||||
Mayoral elections coming up in 2 weeks
|
||||
</li>
|
||||
<li>
|
||||
New parking meter rates downtown coming tomorrow
|
||||
</li>
|
||||
<li>
|
||||
Park renovations to finish by the next year
|
||||
</li>
|
||||
<li>
|
||||
Annual marathon sign ups available online
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Los resultados son los siguientes:
|
||||
|
||||
1) El usuario ha puesto su nombre como `Tammy` en los parámetros de la petición y ninguna
|
||||
preferencia: 
|
||||
2) El usuario ha puesto su nombre como `Johnny` en los parámetros de la petición y tiene preferencia por noticias del
|
||||
mundo, negocios y ciencia: 
|
||||
|
||||
Las distintas subvistas como `worldNews.jsp`, `businessNews.jsp`, etc. se incluyen condicionalmente en función de los
|
||||
parámetros de la solicitud.
|
||||
|
||||
**Cómo utilizarlo**
|
||||
|
||||
Para probar este ejemplo, asegúrese de tener Tomcat 10+ instalado. Configure su IDE para construir un archivo WAR a
|
||||
partir del módulo y despliegue ese archivo en el servidor
|
||||
|
||||
IntelliJ:
|
||||
|
||||
En `Run` y `edit configurations` Asegúrate de que el servidor Tomcat es una de las configuraciones de ejecución. Vaya a
|
||||
la pestaña de despliegue y asegúrese de que se está construyendo un artefacto llamado `composite-view:war exploded`. Si
|
||||
no está presente, añada uno.
|
||||
|
||||
Asegúrate de que el artefacto se está construyendo a partir del contenido del directorio `web` y de los resultados de la
|
||||
compilación del módulo. Apunta la salida del artefacto a un lugar conveniente. Ejecute la configuración y vea la página
|
||||
de destino, siga las instrucciones de esa página para continuar.
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
El diagrama de clases aquí muestra el Javabean que es el gestor de vistas. Las vistas son JSP's dentro del directorio
|
||||
web.
|
||||
|
||||
## Aplicabilidad:
|
||||
|
||||
Utiliza el patrón de diseño Composite View cuando:
|
||||
|
||||
## Desea representar jerarquías parciales de objetos.
|
||||
|
||||
* Esperas que las estructuras compuestas puedan incluir nuevos componentes en el futuro.
|
||||
* Desea que los clientes puedan ignorar la diferencia entre composiciones de objetos y objetos individuales. Los
|
||||
clientes tratarán todos los objetos de la estructura compuesta de manera uniforme.
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* Interfaces gráficas de usuario (GUI) en las que los widgets pueden contener otros widgets (por ejemplo, una ventana
|
||||
con paneles, botones y campos de texto).
|
||||
* Estructuras de documentos, como la representación de tablas que contienen filas, que a su vez contienen celdas, todas
|
||||
las cuales pueden tratarse como elementos de una jerarquía unificada.
|
||||
|
||||
## Consecuencias
|
||||
|
||||
Ventajas:
|
||||
|
||||
* Gran flexibilidad a la hora de añadir nuevos componentes: Como los compuestos y los nodos hoja se tratan de manera
|
||||
uniforme, es más fácil añadir nuevos tipos de componentes.
|
||||
* Código cliente simplificado: Los clientes pueden tratar las estructuras compuestas y los elementos individuales de
|
||||
manera uniforme, lo que reduce la complejidad del código cliente.
|
||||
|
||||
Contrapartidas:
|
||||
|
||||
* Generalización excesiva: El diseño del sistema puede volverse más complejo si haces que todo sea compuesto,
|
||||
especialmente si tu aplicación no lo requiere.
|
||||
* Dificultad en la aplicación de restricciones: Puede ser más difícil restringir los componentes de un compuesto a sólo
|
||||
ciertos tipos.
|
||||
|
||||
## Patrones Relacionados
|
||||
|
||||
* [Decorator](https://java-design-patterns.com/patterns/decorator/): Mientras que Decorator se utiliza para añadir
|
||||
responsabilidades a los objetos, Composite está pensado para construir estructuras de objetos.
|
||||
* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Composite puede combinarse a menudo con Flyweight
|
||||
para implementar nodos hoja compartidos en una estructura compuesta, reduciendo la huella de memoria.
|
||||
* [Cadena de responsabilidad](https://java-design-patterns.com/patterns/chain-of-responsibility/): Puede usarse con
|
||||
Composite para permitir a los componentes pasar peticiones a través de la jerarquía.
|
||||
* [Composite](https://java-design-patterns.com/patterns/composite/)
|
||||
* [Ayudante de vista](https://www.oracle.com/java/technologies/viewhelper.html)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Core J2EE Patterns - Composite View](https://www.oracle.com/java/technologies/composite-view.html)
|
||||
* [Composite View Design Pattern – Core J2EE Patterns](https://www.dineshonjava.com/composite-view-design-pattern/)
|
||||
* [Patterns of Enterprise Application Architecture](https://amzn.to/49jpQG3)
|
||||
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/3xfntGJ)
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,229 @@
|
||||
---
|
||||
title: Composite
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Gang of Four
|
||||
- Object composition
|
||||
- Recursion
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
* Object Tree
|
||||
* Composite Structure
|
||||
|
||||
## Propósito
|
||||
|
||||
Componga objetos en estructuras de árbol para representar jerarquías parte-todo. Composite permite a los clientes tratar
|
||||
objetos individuales y composiciones de objetos de manera uniforme.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
|
||||
> Cada frase se compone de palabras que a su vez se componen de caracteres. Cada uno de estos objetos es imprimible y
|
||||
> puede tener algo impreso antes o después de ellos, como la frase siempre termina con punto final y la palabra siempre
|
||||
> tiene espacio antes de ella.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón compuesto permite a los clientes tratar uniformemente los objetos individuales.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> En ingeniería de software, el patrón compuesto es un patrón de diseño de partición. El patrón compuesto describe que
|
||||
> un grupo de objetos debe ser tratado de la misma manera que una única instancia de un objeto. La intención de un
|
||||
> compuesto es "componer" objetos en estructuras de árbol para representar jerarquías parte-todo. La implementación del
|
||||
> patrón de composición permite a los clientes tratar los objetos individuales y las composiciones de manera uniforme.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tomando nuestro ejemplo anterior. Aquí tenemos la clase base `LetterComposite` y los diferentes tipos
|
||||
imprimibles `Letter`, `Word` y `Sentence`.
|
||||
|
||||
```java
|
||||
public abstract class LetterComposite {
|
||||
|
||||
private final List<LetterComposite> children = new ArrayList<>();
|
||||
|
||||
public void add(LetterComposite letter) {
|
||||
children.add(letter);
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return children.size();
|
||||
}
|
||||
|
||||
protected void printThisBefore() {
|
||||
}
|
||||
|
||||
protected void printThisAfter() {
|
||||
}
|
||||
|
||||
public void print() {
|
||||
printThisBefore();
|
||||
children.forEach(LetterComposite::print);
|
||||
printThisAfter();
|
||||
}
|
||||
}
|
||||
|
||||
public class Letter extends LetterComposite {
|
||||
|
||||
private final char character;
|
||||
|
||||
public Letter(char c) {
|
||||
this.character = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printThisBefore() {
|
||||
System.out.print(character);
|
||||
}
|
||||
}
|
||||
|
||||
public class Word extends LetterComposite {
|
||||
|
||||
public Word(List<Letter> letters) {
|
||||
letters.forEach(this::add);
|
||||
}
|
||||
|
||||
public Word(char... letters) {
|
||||
for (char letter : letters) {
|
||||
this.add(new Letter(letter));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printThisBefore() {
|
||||
System.out.print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
public class Sentence extends LetterComposite {
|
||||
|
||||
public Sentence(List<Word> words) {
|
||||
words.forEach(this::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printThisAfter() {
|
||||
System.out.print(".");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Entonces tenemos un mensajero para llevar mensajes:
|
||||
|
||||
```java
|
||||
public class Messenger {
|
||||
|
||||
LetterComposite messageFromOrcs() {
|
||||
|
||||
var words = List.of(
|
||||
new Word('W', 'h', 'e', 'r', 'e'),
|
||||
new Word('t', 'h', 'e', 'r', 'e'),
|
||||
new Word('i', 's'),
|
||||
new Word('a'),
|
||||
new Word('w', 'h', 'i', 'p'),
|
||||
new Word('t', 'h', 'e', 'r', 'e'),
|
||||
new Word('i', 's'),
|
||||
new Word('a'),
|
||||
new Word('w', 'a', 'y')
|
||||
);
|
||||
|
||||
return new Sentence(words);
|
||||
|
||||
}
|
||||
|
||||
LetterComposite messageFromElves() {
|
||||
|
||||
var words = List.of(
|
||||
new Word('M', 'u', 'c', 'h'),
|
||||
new Word('w', 'i', 'n', 'd'),
|
||||
new Word('p', 'o', 'u', 'r', 's'),
|
||||
new Word('f', 'r', 'o', 'm'),
|
||||
new Word('y', 'o', 'u', 'r'),
|
||||
new Word('m', 'o', 'u', 't', 'h')
|
||||
);
|
||||
|
||||
return new Sentence(words);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Y entonces se puede utilizar como:
|
||||
|
||||
```java
|
||||
var messenger=new Messenger();
|
||||
|
||||
LOGGER.info("Message from the orcs: ");
|
||||
messenger.messageFromOrcs().print();
|
||||
|
||||
LOGGER.info("Message from the elves: ");
|
||||
messenger.messageFromElves().print();
|
||||
```
|
||||
|
||||
La salida de la consola:
|
||||
|
||||
```
|
||||
Message from the orcs:
|
||||
Where there is a whip there is a way.
|
||||
Message from the elves:
|
||||
Much wind pours from your mouth.
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Composite cuando
|
||||
|
||||
* Desea representar jerarquías parciales de objetos.
|
||||
* Desea que los clientes puedan ignorar la diferencia entre composiciones de objetos y objetos individuales. Los
|
||||
clientes tratarán todos los objetos de la estructura compuesta de manera uniforme.
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* Interfaces gráficas de usuario donde los componentes pueden contener otros componentes (por ejemplo, paneles que
|
||||
contienen botones, etiquetas, otros paneles).
|
||||
* Representaciones de sistemas de archivos donde los directorios pueden contener archivos y otros directorios.
|
||||
* Estructuras organizativas en las que un departamento puede contener subdepartamentos y empleados.
|
||||
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html)
|
||||
y [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
|
||||
* Árbol de componentes [Apache Wicket](https://github.com/apache/wicket),
|
||||
ver [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java)
|
||||
y [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
|
||||
|
||||
## Consecuencias
|
||||
|
||||
Ventajas:
|
||||
|
||||
* Simplifica el código cliente, ya que puede tratar estructuras compuestas y objetos individuales de manera uniforme.
|
||||
* Facilita la adición de nuevos tipos de componentes, ya que no es necesario modificar el código existente.
|
||||
|
||||
Contrapartidas:
|
||||
|
||||
* Puede hacer que el diseño sea demasiado general. Puede ser difícil restringir los componentes de un compuesto.
|
||||
* Puede dificultar la restricción de los tipos de componentes de un compuesto.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Composite puede usar Flyweight para compartir
|
||||
instancias de componentes entre varios composites.
|
||||
* [Iterador](https://java-design-patterns.com/patterns/iterator/): Puede ser utilizado para atravesar estructuras
|
||||
Composite.
|
||||
* [Visitante](https://java-design-patterns.com/patterns/visitor/): Puede aplicar una operación sobre una estructura
|
||||
Composite.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3xoLAmi)
|
||||
* [Patterns of Enterprise Application Architecture](https://amzn.to/3vBKXWb)
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Curiously Recurring Template Pattern
|
||||
language: es
|
||||
category: Structural
|
||||
tag:
|
||||
- Extensibility
|
||||
- Instantiation
|
||||
---
|
||||
|
||||
## Nombre / clasificación
|
||||
|
||||
Curiously Recurring Template Pattern
|
||||
|
||||
## También conocido como
|
||||
|
||||
Recursive Type Bound, Recursive Generic
|
||||
|
||||
## Propósito
|
||||
|
||||
Permitir que los componentes derivados hereden ciertas funcionalidades de un componente base que sean compatibles con el tipo derivado.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> Para una promoción de artes marciales mixtas que esté planificando un evento, es crucial asegurarse de que los combates se organicen entre atletas de la misma categoría de peso. Así se evitan los enfrentamientos entre luchadores de tallas muy diferentes, como un peso pesado contra un peso gallo.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> Hacer que ciertos métodos dentro de un tipo acepten argumentos específicos de sus subtipos.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> El patrón de plantilla curiosamente recurrente (CRTP) es un modismo, originalmente en C++, en el que una clase X deriva de una instanciación de plantilla de clase usando la propia X como argumento de plantilla.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Definamos la interfaz genérica Fighter
|
||||
|
||||
```java
|
||||
public interface Fighter<T> {
|
||||
|
||||
void fight(T t);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
La clase `MMAFighter` se utiliza para crear luchadores que se distinguen por su categoría de peso.
|
||||
|
||||
```java
|
||||
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
|
||||
|
||||
private final String name;
|
||||
private final String surname;
|
||||
private final String nickName;
|
||||
private final String speciality;
|
||||
|
||||
public MmaFighter(String name, String surname, String nickName, String speciality) {
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
this.nickName = nickName;
|
||||
this.speciality = speciality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fight(T opponent) {
|
||||
LOGGER.info("{} is going to fight against {}", this, opponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " \"" + nickName + "\" " + surname;
|
||||
}
|
||||
```
|
||||
|
||||
Los siguientes son algunos subtipos de `MmaFighter`
|
||||
|
||||
```java
|
||||
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
|
||||
|
||||
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
|
||||
super(name, surname, nickName, speciality);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
|
||||
|
||||
public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
|
||||
super(name, surname, nickName, speciality);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Un luchador puede enfrentarse a un oponente de la misma categoría de peso, si el oponente es de una categoría de peso diferente
|
||||
se produce un error.
|
||||
|
||||
```java
|
||||
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
|
||||
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
|
||||
fighter1.fight(fighter2); // This is fine
|
||||
|
||||
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
|
||||
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
|
||||
fighter3.fight(fighter4); // This is fine too
|
||||
|
||||
fighter1.fight(fighter3); // This will raise a compilation error
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el Patrón de Plantilla Curiosamente Recurrente cuando
|
||||
|
||||
* Tienes conflictos de tipos al encadenar métodos en una jerarquía de objetos
|
||||
* Desea utilizar un método de clase parametrizado que pueda aceptar subclases de la clase como argumentos, permitiendo que se aplique a objetos que heredan de la clase
|
||||
* Desea que ciertos métodos funcionen sólo con instancias del mismo tipo, por ejemplo, para lograr la comparabilidad mutua.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [El blog de NuaH](https://nuah.livejournal.com/328187.html)
|
||||
* Respuesta de Yogesh Umesh Vaity a [¿Qué significa "Recursive type bound" en Generics?](https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean)
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* [java.lang.Enum](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [How do I decrypt "Enum<E extends Enum\<E>>"?](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106)
|
||||
* Chapter 5 Generics, Item 30 in [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
|
||||
|
After Width: | Height: | Size: 120 KiB |
@@ -0,0 +1,169 @@
|
||||
---
|
||||
title: Decorator
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Gang of Four
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
Wrapper
|
||||
|
||||
## Propósito
|
||||
|
||||
Adjunte responsabilidades adicionales a un objeto de forma dinámica. Los decoradores proporcionan una alternativa
|
||||
flexible a la subclase para ampliar la funcionalidad.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> En las colinas cercanas vive un trol furioso. Normalmente va con las manos desnudas, pero a veces lleva un arma. Para
|
||||
> armar al troll no es necesario crear un nuevo troll sino decorarlo dinámicamente con un arma adecuada.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón decorador permite cambiar dinámicamente el comportamiento de un objeto en tiempo de ejecución envolviéndolo
|
||||
> en un objeto de una clase decoradora.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> En programación orientada a objetos, el patrón decorador es un patrón de diseño que permite añadir comportamiento a un
|
||||
> objeto individual, ya sea de forma estática o dinámica, sin afectar al comportamiento de otros objetos de la misma
|
||||
> clase. El patrón decorador suele ser útil para adherirse al Principio de Responsabilidad Única, ya que permite dividir
|
||||
> la funcionalidad entre clases con áreas de interés únicas, así como al Principio Abierto-Cerrado, al permitir extender
|
||||
> la funcionalidad de una clase sin modificarla.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tomemos el ejemplo del troll. En primer lugar tenemos un `SimpleTroll` que implementa la interfaz `Troll`:
|
||||
|
||||
```java
|
||||
public interface Troll {
|
||||
void atacar();
|
||||
int getPoderAtaque();
|
||||
void huirBatalla();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class SimpleTroll implements Troll {
|
||||
|
||||
@Override
|
||||
public void atacar() {
|
||||
LOGGER.info("¡El troll intenta atraparte!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPoderAtaque() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void huirBatalla() {
|
||||
LOGGER.info("¡El troll chilla de horror y huye!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A continuación, queremos añadir un palo para el troll. Podemos hacerlo de forma dinámica mediante el uso de un
|
||||
decorador:
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class TrollConGarrote implements Troll {
|
||||
|
||||
private final Troll decorado;
|
||||
|
||||
public TrollConGarrote(Troll decorado) {
|
||||
this.decorado = decorado;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void atacar() {
|
||||
decorado.atacar();
|
||||
LOGGER.info("¡El troll te golpea con un garrote!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPoderAtaque() {
|
||||
return decorado.getPoderAtaque() + 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void huirBatalla() {
|
||||
decorado.huirBatalla();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Aquí está el troll en acción:
|
||||
|
||||
```java
|
||||
// troll simple
|
||||
LOGGER.info("Un troll de aspecto simple se acerca.");
|
||||
var troll = new SimpleTroll();
|
||||
troll.atacar();
|
||||
troll.huirBatalla();
|
||||
LOGGER.info("Poder del troll simple: {}.\n", troll.getPoderAtaque());
|
||||
|
||||
// cambia el comportamiento del troll simple agregando un decorador
|
||||
LOGGER.info("Un troll con un enorme garrote te sorprende.");
|
||||
var trollConGarrote = new TrollConGarrote(troll);
|
||||
trollConGarrote.atacar();
|
||||
trollConGarrote.huirBatalla();
|
||||
LOGGER.info("Poder del troll con garrote: {}.\n", trollConGarrote.getPoderAtaque());
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```java
|
||||
Un troll de aspecto simple se acerca.
|
||||
¡El troll intenta atraparte!
|
||||
¡El troll chilla de horror y huye!
|
||||
Poder del troll simple: 10.
|
||||
|
||||
Un troll con un enorme garrote te sorprende.
|
||||
¡El troll intenta atraparte!
|
||||
¡El troll te golpea con un garrote!
|
||||
¡El troll chilla de horror y huye!
|
||||
Poder del troll con garrote: 20.
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Decorator se utiliza para:
|
||||
|
||||
* Añadir responsabilidades a objetos individuales de forma dinámica y transparente, es decir, sin
|
||||
afectar a otros objetos.
|
||||
* Para responsabilidades que pueden ser retiradas.
|
||||
* Cuando la extensión por subclase es poco práctica. A veces es posible un gran número de extensiones independientes
|
||||
son posibles y producirían una explosión de subclases para soportar cada combinación. O la definición de una clase
|
||||
puede estar oculta o no estar disponible para subclases.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Decorator Pattern Tutorial](https://www.journaldev.com/1540/decorator-design-pattern-in-java-example)
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html),
|
||||
[java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html)
|
||||
y [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html)
|
||||
* [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-)
|
||||
* [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-)
|
||||
* [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b)
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: Delegation
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
Proxy Pattern
|
||||
|
||||
## Propósito
|
||||
|
||||
Es una técnica en la que un objeto expresa cierto comportamiento al exterior pero en realidad delega la responsabilidad
|
||||
de implementar ese comportamiento en un objeto asociado.
|
||||
|
||||
## Explanation
|
||||
|
||||
Real-world example
|
||||
|
||||
> Imaginemos que tenemos aventureros que luchan contra monstruos con diferentes armas dependiendo de sus habilidades y
|
||||
> destrezas. Debemos ser capaces de equiparles con diferentes sin tener que modificar su código fuente para cada una de
|
||||
> ellas. El patrón de delegación lo hace posible delegando el trabajo dinámico a un objeto específico que implementa una
|
||||
> interfaz con métodos relevantes.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> En programación orientada a objetos, la delegación se refiere a la evaluación de un miembro (propiedad o método) de un
|
||||
> objeto (el receptor) en el contexto de otro objeto original (el emisor). La delegación puede hacerse explícitamente,
|
||||
> pasando el objeto emisor al objeto receptor, lo que puede hacerse en cualquier lenguaje orientado a objetos; o
|
||||
> implícitamente, mediante las reglas de búsqueda de miembros del lenguaje, lo que requiere soporte del lenguaje para la
|
||||
> función.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tenemos una interfaz `Printer` y tres implementaciones `CanonPrinter`, `EpsonPrinter` y `HpPrinter`.
|
||||
|
||||
```java
|
||||
public interface Printer {
|
||||
void print(final String message);
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class CanonPrinter implements Printer {
|
||||
@Override
|
||||
public void print(String message) {
|
||||
LOGGER.info("Canon Printer : {}", message);
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class EpsonPrinter implements Printer {
|
||||
@Override
|
||||
public void print(String message) {
|
||||
LOGGER.info("Epson Printer : {}", message);
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class HpPrinter implements Printer {
|
||||
@Override
|
||||
public void print(String message) {
|
||||
LOGGER.info("HP Printer : {}", message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
El `PrinterController` puede ser utilizado como un `Printer` delegando cualquier trabajo manejado por este
|
||||
a un objeto que la implemente.
|
||||
|
||||
```java
|
||||
public class PrinterController implements Printer {
|
||||
|
||||
private final Printer printer;
|
||||
|
||||
public PrinterController(Printer printer) {
|
||||
this.printer = printer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String message) {
|
||||
printer.print(message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now on the client code printer controllers can print messages differently depending on the
|
||||
object they're delegating that work to.
|
||||
|
||||
```java
|
||||
private static final String MESSAGE_TO_PRINT = "hello world";
|
||||
|
||||
var hpPrinterController = new PrinterController(new HpPrinter());
|
||||
var canonPrinterController = new PrinterController(new CanonPrinter());
|
||||
var epsonPrinterController = new PrinterController(new EpsonPrinter());
|
||||
|
||||
hpPrinterController.print(MESSAGE_TO_PRINT);
|
||||
canonPrinterController.print(MESSAGE_TO_PRINT);
|
||||
epsonPrinterController.print(MESSAGE_TO_PRINT)
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```java
|
||||
HP Printer : hello world
|
||||
Canon Printer : hello world
|
||||
Epson Printer : hello world
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Delegate para conseguir lo siguiente
|
||||
|
||||
* Reducir el acoplamiento de los métodos a su clase
|
||||
* Componentes que se comportan de forma idéntica, pero teniendo en cuenta que esta situación puede cambiar en el futuro.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern)
|
||||
* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern)
|
||||
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: Embedded Value
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Data Access
|
||||
- Enterprise Application Pattern
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
Asignación agregada, Compositor
|
||||
|
||||
## Propósito
|
||||
|
||||
Muchos objetos pequeños tienen sentido en un sistema OO que no tienen sentido como tablas en una base de datos. Un valor
|
||||
incrustado asigna los valores de un objeto a campos del registro del propietario del objeto.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> Algunos ejemplos son los objetos monetarios y los intervalos de fechas. Aunque el pensamiento por defecto es guardar
|
||||
> un objeto como una tabla, ninguna persona en su sano juicio querría una tabla de valores monetarios.
|
||||
> Otro ejemplo serían los pedidos online que tienen una dirección de envío como calle, ciudad, estado. Asignamos estos
|
||||
> valores del objeto Dirección de envío a los campos del registro del objeto Pedido.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón de valores incrustados permite asignar un objeto a varios campos de la tabla de otro objeto.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Consideremos el ejemplo de un pedido online donde tenemos detalles del artículo pedido y la dirección de envío. Tenemos
|
||||
la dirección de envío incrustada en el objeto Pedido. Pero en la base de datos asignamos los valores de la dirección de
|
||||
envío en el registro del pedido en lugar de crear una tabla separada para la dirección de envío y utilizar una clave
|
||||
externa para hacer referencia al objeto del pedido.
|
||||
|
||||
Primero, tenemos POJOs `Order` y `ShippingAddress`.
|
||||
|
||||
```java
|
||||
public class Order {
|
||||
|
||||
private int id;
|
||||
private String item;
|
||||
private String orderedBy;
|
||||
private ShippingAddress ShippingAddress;
|
||||
|
||||
public Order(String item, String orderedBy, ShippingAddress ShippingAddress) {
|
||||
this.item = item;
|
||||
this.orderedBy = orderedBy;
|
||||
this.ShippingAddress = ShippingAddress;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public class ShippingAddress {
|
||||
|
||||
private String city;
|
||||
private String state;
|
||||
private String pincode;
|
||||
|
||||
public ShippingAddress(String city, String state, String pincode) {
|
||||
this.city = city;
|
||||
this.state = state;
|
||||
this.pincode = pincode;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ahora, tenemos que crear sólo una tabla para el Pedido junto con los campos para los atributos de la dirección de envío.
|
||||
|
||||
```Sql
|
||||
CREATE TABLE Orders (Id INT AUTO_INCREMENT, item VARCHAR(50) NOT NULL, orderedBy VARCHAR(50) city VARCHAR(50), state VARCHAR(50), pincode CHAR(6) NOT NULL, PRIMARY KEY(Id))
|
||||
```
|
||||
|
||||
Mientras realizamos las consultas e inserciones en la base de datos, encasillamos y desencasillamos los detalles de las
|
||||
direcciones de envío.
|
||||
|
||||
```java
|
||||
final String INSERT_ORDER = "INSERT INTO Orders (item, orderedBy, city, state, pincode) VALUES (?, ?, ?, ?, ?)";
|
||||
|
||||
public boolean insertOrder(Order order) throws Exception {
|
||||
var insertOrder = new PreparedStatement(INSERT_ORDER);
|
||||
var address = order.getShippingAddress();
|
||||
conn.setAutoCommit(false);
|
||||
insertIntoOrders.setString(1, order.getItem());
|
||||
insertIntoOrders.setString(2, order.getOrderedBy());
|
||||
insertIntoOrders.setString(3, address.getCity());
|
||||
insertIntoOrders.setString(4, address.getState());
|
||||
insertIntoOrders.setString(5, address.getPincode());
|
||||
|
||||
var affectedRows = insertIntoOrders.executeUpdate();
|
||||
if(affectedRows == 1){
|
||||
Logger.info("Inserted successfully");
|
||||
}else{
|
||||
Logger.info("Couldn't insert " + order);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Valor incrustado cuando
|
||||
|
||||
* Muchos objetos pequeños tienen sentido en un sistema OO que no tienen sentido como tablas en una base de datos.
|
||||
* Los casos más simples para Embedded Value son los claros y simples Value Objects como dinero y rango de fechas.
|
||||
* Si está mapeando a un esquema existente, puede utilizar este patrón cuando una tabla contiene datos que desea dividir
|
||||
en más de un objeto en memoria. Esto puede ocurrir cuando se desea factorizar algún comportamiento en el modelo de
|
||||
objetos.
|
||||
* En la mayoría de los casos, sólo utilizará el valor incrustado en un objeto de referencia cuando la asociación entre
|
||||
ellos tenga un único valor en ambos extremos (una asociación uno a uno).
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Dzone](https://dzone.com/articles/practical-php-patterns/practical-php-patterns-3)
|
||||
* [Ram N Java](https://ramj2ee.blogspot.com/2013/08/embedded-value-design-pattern.html)
|
||||
* [Five's Weblog](https://powerdream5.wordpress.com/2007/10/09/embedded-value/)
|
||||
|
||||
## Consecuencias
|
||||
|
||||
* La gran ventaja del valor incrustado es que permite realizar consultas SQL contra los valores del objeto dependiente.
|
||||
* El objeto valor incrustado no tiene ningún comportamiento de persistencia.
|
||||
* Al usar esto, tienes que tener cuidado de que cualquier cambio en el dependiente marque al propietario como sucio-lo
|
||||
cual no es un problema con los Objetos de Valor que son reemplazados en el propietario.
|
||||
* Otro problema es la carga y el guardado. Si sólo carga la memoria del objeto incrustado cuando carga el propietario,
|
||||
eso es un argumento para guardar ambos en la misma tabla.
|
||||
* Otra cuestión es si querrás acceder a los datos de los objetos incrustados por separado a través de SQL. Esto puede
|
||||
ser importante si estás haciendo informes a través de SQL y no tienes una base de datos separada para los informes.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Fowler, Martin - Patterns of enterprise application architecture-Addison-Wesley](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
|
||||
* [Ram N Java](https://ramj2ee.blogspot.com/2013/08/embedded-value-design-pattern.html)
|
||||
|
After Width: | Height: | Size: 105 KiB |
@@ -0,0 +1,170 @@
|
||||
---
|
||||
title: Event Aggregator
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Reactive
|
||||
---
|
||||
|
||||
## Nombre
|
||||
|
||||
Agregador de eventos
|
||||
|
||||
## Intención
|
||||
|
||||
Un sistema con muchos objetos puede resultar complejo cuando un cliente quiere suscribirse a eventos. El cliente tiene
|
||||
que encontrar y registrarse para
|
||||
cada objeto individualmente, si cada objeto tiene múltiples eventos entonces cada evento requiere una suscripción
|
||||
separada. Un Agregador de Eventos actúa como una única fuente
|
||||
de eventos para muchos objetos. Se registra para todos los eventos de los muchos objetos permitiendo a los clientes
|
||||
registrarse sólo con el agregador.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> El rey Joffrey se sienta en el trono de hierro y gobierna los siete reinos de Poniente. Recibe la mayor parte de su
|
||||
> información crítica de Mano del Rey, el segundo al mando. La Mano del Rey tiene muchos consejeros cercanos que le
|
||||
> proporcionan información relevante sobre los acontecimientos que ocurren en el reino.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El Agregador de Eventos es un mediador de eventos que recoge eventos de múltiples fuentes y los entrega a los
|
||||
> observadores registrados.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
En nuestro ejemplo programático, demostramos la implementación de un patrón agregador de eventos. Algunos de los objetos
|
||||
son escuchadores de eventos, otros son emisores de eventos, y el agregador de eventos hace ambas cosas.
|
||||
|
||||
```java
|
||||
public interface EventObserver {
|
||||
void onEvent(Event e);
|
||||
}
|
||||
|
||||
public abstract class EventEmitter {
|
||||
|
||||
private final Map<Event, List<EventObserver>> observerLists;
|
||||
|
||||
public EventEmitter() {
|
||||
observerLists = new HashMap<>();
|
||||
}
|
||||
|
||||
public final void registerObserver(EventObserver obs, Event e) {
|
||||
...
|
||||
}
|
||||
|
||||
protected void notifyObservers(Event e) {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`KingJoffrey` está escuchando eventos de `KingsHand`.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class KingJoffrey implements EventObserver {
|
||||
@Override
|
||||
public void onEvent(Event e) {
|
||||
LOGGER.info("Received event from the King's Hand: {}", e.toString());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
El `ReyMano` está escuchando los acontecimientos de sus subordinados `LordBaelish`, `LordVarys` y `Scout`.
|
||||
Lo que escuche de ellos, se lo entrega al "Rey Joffrey".
|
||||
|
||||
```java
|
||||
public class KingsHand extends EventEmitter implements EventObserver {
|
||||
|
||||
public KingsHand() {
|
||||
}
|
||||
|
||||
public KingsHand(EventObserver obs, Event e) {
|
||||
super(obs, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event e) {
|
||||
notifyObservers(e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Por ejemplo, `LordVarys` encuentra un traidor cada domingo y lo notifica al `KingsHand`.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class LordVarys extends EventEmitter implements EventObserver {
|
||||
@Override
|
||||
public void timePasses(Weekday day) {
|
||||
if (day == Weekday.SATURDAY) {
|
||||
notifyObservers(Event.TRAITOR_DETECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
El siguiente fragmento muestra cómo se construyen y conectan los objetos.
|
||||
|
||||
```java
|
||||
var kingJoffrey = new KingJoffrey();
|
||||
|
||||
var kingsHand = new KingsHand();
|
||||
kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
|
||||
kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
|
||||
kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
|
||||
kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);
|
||||
|
||||
var varys = new LordVarys();
|
||||
varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
|
||||
varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);
|
||||
|
||||
var scout = new Scout();
|
||||
scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
|
||||
scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);
|
||||
|
||||
var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);
|
||||
|
||||
var emitters = List.of(
|
||||
kingsHand,
|
||||
baelish,
|
||||
varys,
|
||||
scout
|
||||
);
|
||||
|
||||
Arrays.stream(Weekday.values())
|
||||
.<Consumer<? super EventEmitter>>map(day -> emitter -> emitter.timePasses(day))
|
||||
.forEachOrdered(emitters::forEach);
|
||||
```
|
||||
|
||||
La salida de la consola después de ejecutar el ejemplo.
|
||||
|
||||
```
|
||||
18:21:52.955 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Warships approaching
|
||||
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: White walkers sighted
|
||||
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Stark sighted
|
||||
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Traitor detected
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Agregador de eventos cuando
|
||||
|
||||
* El Agregador de Eventos es una buena opción cuando tienes muchos objetos que son fuentes potenciales de eventos. En
|
||||
lugar de hacer que el observador se ocupe de registrarse con todos ellos, puedes centralizar la lógica de registro en
|
||||
el Agregador de Eventos. Además de simplificar el registro, un Agregador de Eventos también simplifica los problemas
|
||||
de gestión de memoria en el uso de observadores.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Observer](https://java-design-patterns.com/patterns/observer/)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html)
|
||||
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,224 @@
|
||||
---
|
||||
title: Facade
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Gang Of Four
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
|
||||
Proporcionar una interfaz unificada a un conjunto de interfaces de un subsistema. La fachada define una interfaz que
|
||||
facilita el uso del subsistema.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> ¿Cómo funciona una mina de oro? "¡Bueno, los mineros bajan y sacan oro!" dices. Eso es lo que crees porque estás
|
||||
> usando una interfaz simple que la mina de oro proporciona en el exterior, internamente tiene que hacer un montón de
|
||||
> cosas para que suceda. Esta interfaz simple al subsistema complejo es una fachada.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón Facade proporciona una interfaz simplificada a un subsistema complejo.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> Una fachada es un objeto que proporciona una interfaz simplificada a un cuerpo de código mayor, como una biblioteca de
|
||||
> clases.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tomemos el ejemplo de la mina de oro. Aquí tenemos la jerarquía de los trabajadores enanos de la mina. Primero hay una
|
||||
clase base `DwarvenMineWorker`:
|
||||
|
||||
```java
|
||||
|
||||
@Slf4j
|
||||
public abstract class DwarvenMineWorker {
|
||||
|
||||
public void goToSleep() {
|
||||
LOGGER.info("{} goes to sleep.", name());
|
||||
}
|
||||
|
||||
public void wakeUp() {
|
||||
LOGGER.info("{} wakes up.", name());
|
||||
}
|
||||
|
||||
public void goHome() {
|
||||
LOGGER.info("{} goes home.", name());
|
||||
}
|
||||
|
||||
public void goToMine() {
|
||||
LOGGER.info("{} goes to the mine.", name());
|
||||
}
|
||||
|
||||
private void action(Action action) {
|
||||
switch (action) {
|
||||
case GO_TO_SLEEP -> goToSleep();
|
||||
case WAKE_UP -> wakeUp();
|
||||
case GO_HOME -> goHome();
|
||||
case GO_TO_MINE -> goToMine();
|
||||
case WORK -> work();
|
||||
default -> LOGGER.info("Undefined action");
|
||||
}
|
||||
}
|
||||
|
||||
public void action(Action... actions) {
|
||||
Arrays.stream(actions).forEach(this::action);
|
||||
}
|
||||
|
||||
public abstract void work();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
enum Action {
|
||||
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Luego tenemos las clases concretas enanas `DwarvenTunnelDigger`, `DwarvenGoldDigger` y `DwarvenCartOperator`:
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class DwarvenTunnelDigger extends DwarvenMineWorker {
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} creates another promising tunnel.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarven tunnel digger";
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class DwarvenGoldDigger extends DwarvenMineWorker {
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} digs for gold.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarf gold digger";
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class DwarvenCartOperator extends DwarvenMineWorker {
|
||||
|
||||
@Override
|
||||
public void work() {
|
||||
LOGGER.info("{} moves gold chunks out of the mine.", name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Dwarf cart operator";
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Para manejar a todos estos trabajadores de la mina de oro tenemos la `FachadaDwarvenGoldmine`:
|
||||
|
||||
```java
|
||||
public class DwarvenGoldmineFacade {
|
||||
|
||||
private final List<DwarvenMineWorker> workers;
|
||||
|
||||
public DwarvenGoldmineFacade() {
|
||||
workers = List.of(
|
||||
new DwarvenGoldDigger(),
|
||||
new DwarvenCartOperator(),
|
||||
new DwarvenTunnelDigger());
|
||||
}
|
||||
|
||||
public void startNewDay() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE);
|
||||
}
|
||||
|
||||
public void digOutGold() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.WORK);
|
||||
}
|
||||
|
||||
public void endDay() {
|
||||
makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP);
|
||||
}
|
||||
|
||||
private static void makeActions(Collection<DwarvenMineWorker> workers,
|
||||
DwarvenMineWorker.Action... actions) {
|
||||
workers.forEach(worker -> worker.action(actions));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ahora vamos a utilizar la fachada:
|
||||
|
||||
```java
|
||||
var facade = new DwarvenGoldmineFacade();
|
||||
facade.startNewDay();
|
||||
facade.digOutGold();
|
||||
facade.endDay();
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```java
|
||||
// Dwarf gold digger wakes up.
|
||||
// Dwarf gold digger goes to the mine.
|
||||
// Dwarf cart operator wakes up.
|
||||
// Dwarf cart operator goes to the mine.
|
||||
// Dwarven tunnel digger wakes up.
|
||||
// Dwarven tunnel digger goes to the mine.
|
||||
// Dwarf gold digger digs for gold.
|
||||
// Dwarf cart operator moves gold chunks out of the mine.
|
||||
// Dwarven tunnel digger creates another promising tunnel.
|
||||
// Dwarf gold digger goes home.
|
||||
// Dwarf gold digger goes to sleep.
|
||||
// Dwarf cart operator goes home.
|
||||
// Dwarf cart operator goes to sleep.
|
||||
// Dwarven tunnel digger goes home.
|
||||
// Dwarven tunnel digger goes to sleep.
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Fachada cuando
|
||||
|
||||
* Quieres proporcionar una interfaz sencilla a un subsistema complejo. Los subsistemas a menudo se vuelven más complejos
|
||||
a medida que evolucionan. La mayoría de los patrones, cuando se aplican, dan como resultado más clases y más pequeñas.
|
||||
Esto hace que el subsistema sea más reutilizable y más fácil de personalizar, pero también se vuelve más difícil de
|
||||
usar para los clientes que no necesitan personalizarlo. Una fachada puede proporcionar una simple vista por defecto
|
||||
del subsistema que es lo suficiente para la mayoría de los clientes. Sólo los clientes que necesiten más
|
||||
personalización tendrán que mirar más allá de la fachada.
|
||||
* Hay muchas dependencias entre los clientes y las clases de implementación de una abstracción.
|
||||
Introducir una fachada para desacoplar el subsistema de los clientes y otros subsistemas, promoviendo así
|
||||
independencia y portabilidad del subsistema.
|
||||
* Quieres estratificar tus subsistemas. Utiliza una fachada para definir un punto de entrada a cada nivel de subsistema.
|
||||
Si los subsistemas son dependientes, puedes simplificar las dependencias entre ellos haciéndolos que se comuniquen
|
||||
entre sí únicamente a través de sus fachadas.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
*[DigitalOcean](https://www.digitalocean.com/community/tutorials/facade-design-pattern-in-java)
|
||||
|
||||
* [Refactoring Guru](https://refactoring.guru/design-patterns/facade)
|
||||
* [GeekforGeeks](https://www.geeksforgeeks.org/facade-design-pattern-introduction/)
|
||||
* [Tutorialspoint](https://www.tutorialspoint.com/design_pattern/facade_pattern.htm)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
After Width: | Height: | Size: 61 KiB |
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Flux
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Flux evita MVC en favor de un flujo de datos unidireccional. Cuando un usuario
|
||||
usuario interactúa con una vista, ésta propaga una acción a través de un
|
||||
central, a los distintos almacenes que contienen los datos de la aplicación y la
|
||||
que actualiza todas las vistas afectadas.
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utilice el patrón Flux cuando
|
||||
|
||||
* Debes centrarte en crear rutas de actualización explícitas y comprensibles para los datos de tu aplicación, lo que simplifica el seguimiento de los cambios durante el desarrollo y facilita la localización y corrección de errores.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Flux - Application architecture for building user interfaces](http://facebook.github.io/flux/)
|
||||
|
After Width: | Height: | Size: 61 KiB |
@@ -0,0 +1,208 @@
|
||||
---
|
||||
title: Flyweight
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Gang of Four
|
||||
- Performance
|
||||
---
|
||||
|
||||
## Propósito
|
||||
|
||||
Utilice la compartición para dar soporte a un gran número de objetos finos de forma eficiente.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> La tienda del alquimista tiene estanterías llenas de pociones mágicas. Muchas de las pociones son las mismas, por lo
|
||||
> que no es necesario crear un nuevo objeto para cada una de ellas. En su lugar, una instancia de objeto puede
|
||||
> representar
|
||||
> múltiples elementos de la estantería para que la huella de memoria siga siendo pequeña.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> Se utiliza para minimizar el uso de memoria o los gastos computacionales compartiendo todo lo posible con objetos
|
||||
> similares.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> En programación informática, flyweight es un patrón de diseño de software. Un flyweight es un objeto que minimiza el
|
||||
> uso de memoria compartiendo tantos datos como sea posible con otros objetos similares; es una forma de utilizar
|
||||
> objetos
|
||||
> en grandes cantidades cuando una simple representación repetida utilizaría una cantidad inaceptable de memoria.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Traduciendo nuestro ejemplo de la tienda de alquimia de arriba. En primer lugar, tenemos diferentes tipos de
|
||||
pociones `Potion`: `HealingPotion`, `HolyWaterPotion` e `InvisibilityPotion`:
|
||||
|
||||
```java
|
||||
public interface Potion {
|
||||
void drink();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class HealingPotion implements Potion {
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class HolyWaterPotion implements Potion {
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class InvisibilityPotion implements Potion {
|
||||
@Override
|
||||
public void drink() {
|
||||
LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Luego la clase Flyweight `PotionFactory`, que es la fábrica para crear pociones.
|
||||
|
||||
```java
|
||||
public class PotionFactory {
|
||||
|
||||
private final Map<PotionType, Potion> potions;
|
||||
|
||||
public PotionFactory() {
|
||||
potions = new EnumMap<>(PotionType.class);
|
||||
}
|
||||
|
||||
Potion createPotion(PotionType type) {
|
||||
var potion = potions.get(type);
|
||||
if (potion == null) {
|
||||
switch (type) {
|
||||
case HEALING -> {
|
||||
potion = new HealingPotion();
|
||||
potions.put(type, potion);
|
||||
}
|
||||
case HOLY_WATER -> {
|
||||
potion = new HolyWaterPotion();
|
||||
potions.put(type, potion);
|
||||
}
|
||||
case INVISIBILITY -> {
|
||||
potion = new InvisibilityPotion();
|
||||
potions.put(type, potion);
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return potion;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`AlchemistShop` contiene dos estantes de pociones mágicas. Las pociones se crean utilizando la antes
|
||||
mencionada `PotionFactory`.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class AlchemistShop {
|
||||
|
||||
private final List<Potion> topShelf;
|
||||
private final List<Potion> bottomShelf;
|
||||
|
||||
public AlchemistShop() {
|
||||
var factory = new PotionFactory();
|
||||
topShelf = List.of(
|
||||
factory.createPotion(PotionType.INVISIBILITY),
|
||||
factory.createPotion(PotionType.INVISIBILITY),
|
||||
factory.createPotion(PotionType.STRENGTH),
|
||||
factory.createPotion(PotionType.HEALING),
|
||||
factory.createPotion(PotionType.INVISIBILITY),
|
||||
factory.createPotion(PotionType.STRENGTH),
|
||||
factory.createPotion(PotionType.HEALING),
|
||||
factory.createPotion(PotionType.HEALING)
|
||||
);
|
||||
bottomShelf = List.of(
|
||||
factory.createPotion(PotionType.POISON),
|
||||
factory.createPotion(PotionType.POISON),
|
||||
factory.createPotion(PotionType.POISON),
|
||||
factory.createPotion(PotionType.HOLY_WATER),
|
||||
factory.createPotion(PotionType.HOLY_WATER)
|
||||
);
|
||||
}
|
||||
|
||||
public final List<Potion> getTopShelf() {
|
||||
return List.copyOf(this.topShelf);
|
||||
}
|
||||
|
||||
public final List<Potion> getBottomShelf() {
|
||||
return List.copyOf(this.bottomShelf);
|
||||
}
|
||||
|
||||
public void drinkPotions() {
|
||||
LOGGER.info("Drinking top shelf potions\n");
|
||||
topShelf.forEach(Potion::drink);
|
||||
LOGGER.info("Drinking bottom shelf potions\n");
|
||||
bottomShelf.forEach(Potion::drink);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
En nuestro escenario, un valiente visitante entra en la tienda del alquimista y se bebe todas las pociones.
|
||||
|
||||
```java
|
||||
// create the alchemist shop with the potions
|
||||
var alchemistShop = new AlchemistShop();
|
||||
// a brave visitor enters the alchemist shop and drinks all the potions
|
||||
alchemistShop.drinkPotions();
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```java
|
||||
Drinking top shelf potions
|
||||
You become invisible. (Potion=1509514333)
|
||||
You become invisible. (Potion=1509514333)
|
||||
You feel strong. (Potion=739498517)
|
||||
You feel healed. (Potion=125130493)
|
||||
You become invisible. (Potion=1509514333)
|
||||
You feel strong. (Potion=739498517)
|
||||
You feel healed. (Potion=125130493)
|
||||
You feel healed. (Potion=125130493)
|
||||
Drinking bottom shelf potions
|
||||
Urgh! This is poisonous. (Potion=166239592)
|
||||
Urgh! This is poisonous. (Potion=166239592)
|
||||
Urgh! This is poisonous. (Potion=166239592)
|
||||
You feel blessed. (Potion=991505714)
|
||||
You feel blessed. (Potion=991505714)
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
La eficacia del patrón Flyweight depende en gran medida de cómo y dónde se utilice. Aplique el patrón
|
||||
Flyweight cuando se cumplan todas las condiciones siguientes:
|
||||
|
||||
* Una aplicación utiliza un gran número de objetos.
|
||||
* Los costes de almacenamiento son altos debido a la gran cantidad de objetos.
|
||||
* La mayor parte del estado de los objetos puede hacerse extrínseco.
|
||||
* Muchos grupos de objetos pueden ser reemplazados por relativamente pocos objetos compartidos una vez que el estado extrínseco
|
||||
extrínseco.
|
||||
* La aplicación no depende de la identidad de los objetos. Dado que los objetos flyweight pueden ser compartidos, las pruebas de identidad
|
||||
devolverán verdadero para objetos conceptualmente distintos.
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) y
|
||||
de forma similar para Byte, Carácter y otros tipos envueltos (Wrappers).
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
After Width: | Height: | Size: 54 KiB |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Front Controller
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Introducir un gestor común para todas las solicitudes de un sitio web. De esta
|
||||
manera podemos encapsular funcionalidad común como la seguridad,
|
||||
internacionalización, enrutamiento y registro en un solo lugar.
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utilice el patrón del controlador frontal cuando
|
||||
|
||||
* Desea encapsular la funcionalidad común de gestión de peticiones en un único lugar.
|
||||
* Desea implementar la gestión dinámica de peticiones, es decir, cambiar el enrutamiento sin modificar el código.
|
||||
* hacer portable la configuración del servidor web, sólo necesitas registrar el manejador de forma específica para el servidor web
|
||||
|
||||
## Ejemplos del mundo real
|
||||
|
||||
* [Apache Struts](https://struts.apache.org/)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
|
||||
* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a)
|
||||
* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94)
|
||||
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: Gateway
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
|
||||
---
|
||||
|
||||
## Propósito
|
||||
|
||||
Proporcionar una interfaz de acceso a un conjunto de sistemas o funcionalidades externas. Gateway proporciona una vista simple y uniforme de
|
||||
recursos externos a los internos de una aplicación.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> Gateway actúa como una verdadera puerta de entrada de una ciudad determinada. Las personas dentro de la ciudad se llaman sistema interno, y las diferentes ciudades externas se llaman servicios externos. La puerta de enlace está aquí para proporcionar acceso al sistema interno a diferentes servicios externos.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> La pasarela puede proporcionar una interfaz que permita al sistema interno utilizar un servicio externo.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> Un servidor que actúa como front-end de la API, recibe solicitudes de la API, aplica políticas de estrangulamiento y seguridad, pasa las solicitudes al servicio back-end y, a continuación, devuelve la respuesta al solicitante.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
La clase principal de nuestro ejemplo es el `ExternalService` que contiene elementos.
|
||||
|
||||
```java
|
||||
class ExternalServiceA implements Gateway {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
LOGGER.info("Executing Service A");
|
||||
// Simulate a time-consuming task
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ExternalServiceB is one of external services.
|
||||
*/
|
||||
class ExternalServiceB implements Gateway {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
LOGGER.info("Executing Service B");
|
||||
// Simulate a time-consuming task
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ExternalServiceC is one of external services.
|
||||
*/
|
||||
class ExternalServiceC implements Gateway {
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
LOGGER.info("Executing Service C");
|
||||
// Simulate a time-consuming task
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
public void error() throws Exception {
|
||||
// Simulate an exception
|
||||
throw new RuntimeException("Service C encountered an error");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Para operar estos servicios externos, aquí está la clase `App`:
|
||||
|
||||
```java
|
||||
public class App {
|
||||
/**
|
||||
* Simulate an application calling external services.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
GatewayFactory gatewayFactory = new GatewayFactory();
|
||||
|
||||
// Register different gateways
|
||||
gatewayFactory.registerGateway("ServiceA", new ExternalServiceA());
|
||||
gatewayFactory.registerGateway("ServiceB", new ExternalServiceB());
|
||||
gatewayFactory.registerGateway("ServiceC", new ExternalServiceC());
|
||||
|
||||
// Use an executor service for asynchronous execution
|
||||
Gateway serviceA = gatewayFactory.getGateway("ServiceA");
|
||||
Gateway serviceB = gatewayFactory.getGateway("ServiceB");
|
||||
Gateway serviceC = gatewayFactory.getGateway("ServiceC");
|
||||
|
||||
// Execute external services
|
||||
try {
|
||||
serviceA.execute();
|
||||
serviceB.execute();
|
||||
serviceC.execute();
|
||||
} catch (ThreadDeath e) {
|
||||
LOGGER.info("Interrupted!" + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
La interfaz `Gateway` es extremadamente sencilla.
|
||||
|
||||
```java
|
||||
interface Gateway {
|
||||
void execute() throws Exception;
|
||||
}
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```java
|
||||
Executing Service A
|
||||
Executing Service B
|
||||
Executing Service C
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilizar el patrón Gateway
|
||||
|
||||
* Para acceder al contenido de un objeto agregado sin exponer su representación interna.
|
||||
* Para la integración con múltiples servicios externos o APIs.
|
||||
* Para proporcionar una interfaz uniforme para recorrer diferentes estructuras de agregados.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Pattern: API Gateway / Backends for Frontends](https://microservices.io/patterns/apigateway.html)
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* [API Gateway](https://java-design-patterns.com/patterns/api-gateway/)
|
||||
* [10 most common use cases of an API Gateway](https://apisix.apache.org/blog/2022/10/27/ten-use-cases-api-gateway/)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Gateway](https://martinfowler.com/articles/gateway-pattern.html)
|
||||
* [What is the difference between Facade and Gateway design patterns?](https://stackoverflow.com/questions/4422211/what-is-the-difference-between-facade-and-gateway-design-patterns)
|
||||
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Marker Interface
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Utilización de interfaces vacías como marcadores para distinguir objetos con un tratamiento especial.
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utilice el patrón de interfaz de marcador cuando
|
||||
|
||||
* Desea identificar los objetos especiales de los objetos normales (para tratarlos de forma diferente)
|
||||
* Desea indicar que un objeto está disponible para cierto tipo de operaciones.
|
||||
|
||||
## Ejemplos del mundo real
|
||||
|
||||
* [javase.8.docs.api.java.io.Serializable](https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html)
|
||||
* [javase.8.docs.api.java.lang.Cloneable](https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)
|
||||
|
After Width: | Height: | Size: 6.3 KiB |
@@ -0,0 +1,164 @@
|
||||
---
|
||||
title: Module
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Propósito
|
||||
El patrón de módulos se utiliza para implementar el concepto de módulos de software, definido por la programación modular, en un lenguaje de programación con soporte directo incompleto para el concepto.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> En una bulliciosa ciudad del software, distintos componentes de software, como la base de datos, la interfaz de usuario y la API, a menudo necesitan colaborar. En lugar de que cada componente hable directamente con los demás, confían en el gestor de módulos. Este gestor de módulos actúa como un mercado central, donde cada componente registra sus servicios y solicitudes para los demás. Esto garantiza que los componentes permanezcan desacoplados y que los cambios en uno de ellos no afecten a todo el sistema.
|
||||
|
||||
|
||||
> Imagina un smartphone moderno. Tiene distintas aplicaciones, como mensajería, cámara y reproductor de música. Aunque cada una funciona de forma independiente, a veces necesitan compartir recursos como el acceso a los contactos o el almacenamiento. En lugar de que cada aplicación tenga su propia forma de acceder a estos recursos, utilizan los módulos integrados del teléfono, como el módulo de contactos o el de almacenamiento. Esto garantiza una experiencia coherente para el usuario y evita posibles conflictos entre aplicaciones.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón Módulo encapsula funciones y datos relacionados en una sola unidad, lo que permite tener componentes de software organizados y manejables.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> En ingeniería de software, el patrón módulo es un patrón de diseño utilizado para implementar el concepto de módulos de software, definido por la programación modular, en un lenguaje de programación con soporte directo incompleto para el concepto.
|
||||
|
||||
> Este patrón se puede implementar de varias maneras dependiendo del lenguaje de programación anfitrión, como el patrón de diseño singleton, miembros estáticos orientados a objetos en una clase y funciones globales de procedimiento. En Python, el patrón está integrado en el lenguaje, y cada archivo .py es automáticamente un módulo. Lo mismo ocurre en Ada, donde el paquete puede considerarse un módulo (similar a una clase estática).
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
```java
|
||||
//Define Logger abstract class
|
||||
abstract class Logger {
|
||||
protected String output;
|
||||
protected String error;
|
||||
|
||||
public abstract void prepare();
|
||||
public abstract void unprepare();
|
||||
public abstract void printString(String message);
|
||||
public abstract void printErrorString(String errorMessage);
|
||||
}
|
||||
|
||||
//File log module
|
||||
class FileLoggerModule extends Logger {
|
||||
private static final String OUTPUT_FILE = "output.log";
|
||||
private static final String ERROR_FILE = "error.log";
|
||||
|
||||
private static FileLoggerModule instance;
|
||||
|
||||
private FileLoggerModule() {
|
||||
this.output = OUTPUT_FILE;
|
||||
this.error = ERROR_FILE;
|
||||
}
|
||||
|
||||
public static FileLoggerModule getSingleton() {
|
||||
if (instance == null) {
|
||||
instance = new FileLoggerModule();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
// For example, open file operation
|
||||
// add the action you want
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unprepare() {
|
||||
// For example, close file operation
|
||||
// add the action you want
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printString(String message) {
|
||||
System.out.println("Writing to " + output + ": " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printErrorString(String errorMessage) {
|
||||
System.out.println("Writing to " + error + ": " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//Console log module
|
||||
class ConsoleLoggerModule extends Logger {
|
||||
private static ConsoleLoggerModule instance;
|
||||
|
||||
private ConsoleLoggerModule() {}
|
||||
|
||||
public static ConsoleLoggerModule getSingleton() {
|
||||
if (instance == null) {
|
||||
instance = new ConsoleLoggerModule();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
//Initialize console operation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unprepare() {
|
||||
//End console operation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printString(String message) {
|
||||
System.out.println("Console Output: " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printErrorString(String errorMessage) {
|
||||
System.err.println("Console Error: " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class App {
|
||||
public void prepare() {
|
||||
FileLoggerModule.getSingleton().prepare();
|
||||
ConsoleLoggerModule.getSingleton().prepare();
|
||||
}
|
||||
|
||||
public void unprepare() {
|
||||
FileLoggerModule.getSingleton().unprepare();
|
||||
ConsoleLoggerModule.getSingleton().unprepare();
|
||||
}
|
||||
|
||||
public void execute(String message) {
|
||||
FileLoggerModule.getSingleton().printString(message);
|
||||
ConsoleLoggerModule.getSingleton().printString(message);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
App app = new App();
|
||||
app.prepare();
|
||||
app.execute("Hello, Module Pattern!");
|
||||
app.unprepare();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Resultados del programa:
|
||||
|
||||
```
|
||||
Writing to output.log: Hello, Module Pattern!
|
||||
Console Output: Hello, Module Pattern!
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
El patrón Módulo puede considerarse un patrón de creación y un patrón estructural. Gestiona la creación y organización de otros elementos, y los agrupa como lo hace el patrón estructural.
|
||||
|
||||
Un objeto que aplica este patrón puede proporcionar el equivalente de un espacio de nombres, proporcionando el proceso de inicialización y finalización de una clase estática o de una clase con miembros estáticos con una sintaxis y una semántica más limpias y concisas.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Module](https://en.wikipedia.org/wiki/Module_pattern)
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: Page Controller
|
||||
category: Structural
|
||||
language: es
|
||||
tags:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## Nombre / clasificación
|
||||
|
||||
Page Controller
|
||||
|
||||
## Propósito
|
||||
|
||||
Se trata de un enfoque de una página que conduce a un archivo lógico que gestiona acciones o peticiones en un sitio web.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> En un sitio web de compras, hay una página de registro para dar de alta un perfil de usuario. Una vez finalizado el registro, la página de registro se redirigirá a una página de usuario para mostrar la información registrada del usuario.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El controlador de página gestiona las peticiones HTTP y los datos en una página específica usando la idea MVC.
|
||||
> La idea es que una página contiene un controlador que maneja el modelo y la vista.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Aquí está el controlador Signup cuando un usuario registra su información para un sitio web.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
@Controller
|
||||
@Component
|
||||
public class SignupController {
|
||||
SignupView view = new SignupView();
|
||||
/**
|
||||
* Signup Controller can handle http request and decide which model and view use.
|
||||
*/
|
||||
SignupController() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle http GET request.
|
||||
*/
|
||||
@GetMapping("/signup")
|
||||
public String getSignup() {
|
||||
return view.display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle http POST request and access model and view.
|
||||
*/
|
||||
@PostMapping("/signup")
|
||||
public String create(SignupModel form, RedirectAttributes redirectAttributes) {
|
||||
LOGGER.info(form.getName());
|
||||
LOGGER.info(form.getEmail());
|
||||
redirectAttributes.addAttribute("name", form.getName());
|
||||
redirectAttributes.addAttribute("email", form.getEmail());
|
||||
redirectAttributes.addFlashAttribute("userInfo", form);
|
||||
return view.redirect(form);
|
||||
}
|
||||
}
|
||||
```
|
||||
Aquí está el modelo y la vista de Signup que son manejados por el controlador de Signup.
|
||||
|
||||
```java
|
||||
@Component
|
||||
@Getter
|
||||
@Setter
|
||||
public class SignupModel {
|
||||
private String name;
|
||||
private String email;
|
||||
private String password;
|
||||
|
||||
public SignupModel() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class SignupView {
|
||||
public SignupView() {
|
||||
}
|
||||
|
||||
public String display() {
|
||||
LOGGER.info("display signup front page");
|
||||
return "/signup";
|
||||
}
|
||||
|
||||
/**
|
||||
* redirect to user page.
|
||||
*/
|
||||
public String redirect(SignupModel form) {
|
||||
LOGGER.info("Redirect to user page with " + "name " + form.getName() + " email " + form.getEmail());
|
||||
return "redirect:/user";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Este es el Controlador de Usuario para manejar la petición Get en una página de usuario.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class UserController {
|
||||
UserView view = new UserView();
|
||||
|
||||
public UserController() {}
|
||||
|
||||
/**
|
||||
* Handle http GET request and access view and model.
|
||||
*/
|
||||
@GetMapping("/user")
|
||||
public String getUserPath(SignupModel form, Model model) {
|
||||
model.addAttribute("name", form.getName());
|
||||
model.addAttribute("email", form.getEmail());
|
||||
return view.display(form);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Aquí están el Modelo de Usuario y la Vista que son manejados por el controlador de Usuario.
|
||||
|
||||
```java
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserModel {
|
||||
private String name;
|
||||
private String email;
|
||||
|
||||
public UserModel() {}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class UserView {
|
||||
/**
|
||||
* displaying command to generate html.
|
||||
* @param user model content.
|
||||
*/
|
||||
public String display(SignupModel user) {
|
||||
LOGGER.info("display user html" + " name " + user.getName() + " email " + user.getEmail());
|
||||
return "/user";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utilice el patrón Page Controller cuando
|
||||
- implementas un sitio donde la mayor parte de la lógica del controlador es simple
|
||||
- implementa un sitio en el que determinadas acciones se gestionan con una página de servidor concreta
|
||||
|
||||
## Créditos
|
||||
- [Page Controller](https://www.martinfowler.com/eaaCatalog/pageController.html)
|
||||
- [Pattern of Enterprise Application Architecture](https://www.martinfowler.com/books/eaa.html)
|
||||
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Page Object
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
# Patrón de objetos de página en Java
|
||||
|
||||
## Ejemplo del mundo real
|
||||
|
||||
Considera un escenario de automatización web donde necesitas interactuar con una página web usando un framework de pruebas como Selenium. El patrón Page Object puede aplicarse para modelar cada página web como una clase Java. Cada clase encapsula la estructura y el comportamiento de la página web correspondiente, facilitando la gestión y actualización del código de automatización.
|
||||
|
||||
## Intención
|
||||
|
||||
Page Object encapsula la interfaz de usuario, ocultando el widget de interfaz de usuario subyacente de una aplicación (normalmente una aplicación web) y proporcionando una API específica de la aplicación para permitir la manipulación de los componentes de interfaz de usuario necesarios para las pruebas. De este modo, permite a la clase de prueba centrarse en la lógica de la prueba.
|
||||
|
||||
## En pocas palabras
|
||||
|
||||
El patrón Page Object en Java es un patrón de diseño utilizado en la automatización de pruebas para representar páginas web como clases Java. Cada clase corresponde a una página web específica y contiene métodos para interactuar con los elementos de esa página. Este patrón mejora el mantenimiento y la legibilidad del código en las pruebas automatizadas.
|
||||
|
||||
## Wikipedia dice
|
||||
|
||||
Aunque no hay una entrada específica en Wikipedia para el patrón Page Object, se utiliza ampliamente en las pruebas de software, en particular en el contexto de la automatización de la interfaz de usuario. El patrón Page Object ayuda a abstraer los detalles de una página web, proporcionando una forma más limpia y fácil de mantener para interactuar con elementos web en pruebas automatizadas.
|
||||
|
||||
## Ejemplo programático
|
||||
|
||||
Vamos a crear un ejemplo programático simple del patrón Page Object para una página de login utilizando Selenium en Java:
|
||||
|
||||
```java
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
public class LoginPage {
|
||||
private final WebDriver driver;
|
||||
|
||||
// Web elements on the login page
|
||||
private final By usernameInput = By.id("username");
|
||||
private final By passwordInput = By.id("password");
|
||||
private final By loginButton = By.id("login-button");
|
||||
|
||||
public LoginPage(WebDriver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
// Methods to interact with the login page
|
||||
|
||||
public void enterUsername(String username) {
|
||||
WebElement usernameElement = driver.findElement(usernameInput);
|
||||
usernameElement.sendKeys(username);
|
||||
}
|
||||
|
||||
public void enterPassword(String password) {
|
||||
WebElement passwordElement = driver.findElement(passwordInput);
|
||||
passwordElement.sendKeys(password);
|
||||
}
|
||||
|
||||
public void clickLoginButton() {
|
||||
WebElement loginButtonElement = driver.findElement(loginButton);
|
||||
loginButtonElement.click();
|
||||
}
|
||||
|
||||
// Other methods specific to the login page if needed
|
||||
}
|
||||
```
|
||||
|
||||
En este ejemplo, la clase `LoginPage` representa la página de login de una aplicación web. Encapsula los elementos web de la página y proporciona métodos para interactuar con esos elementos. La instancia real de Selenium WebDriver se pasa al constructor, permitiendo a los métodos realizar acciones en la página web.
|
||||
|
||||
Este objeto de página se puede utilizar en scripts de prueba para interactuar con la página de inicio de sesión sin exponer los detalles de la estructura de la página en el código de prueba, lo que favorece el mantenimiento y la reutilización.
|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Page Object cuando
|
||||
|
||||
* Estás escribiendo pruebas automatizadas para tu aplicación web y quieres separar la manipulación de la interfaz de usuario necesaria para las pruebas de la lógica de prueba real.
|
||||
* Haga sus pruebas menos frágiles, y más legibles y robustas.
|
||||
|
||||
## Otro ejemplo con diagrama de clases
|
||||

|
||||
|
||||
## Créditos
|
||||
|
||||
* [Martin Fowler - PageObject](http://martinfowler.com/bliki/PageObject.html)
|
||||
* [Selenium - Page Objects](https://github.com/SeleniumHQ/selenium/wiki/PageObjects)
|
||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: Proxy
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Gang Of Four
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
|
||||
Surrogate
|
||||
|
||||
## Propósito
|
||||
|
||||
Proporcionar un sustituto o marcador de posición de otro objeto para controlar el acceso al mismo.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> Imagina una torre donde los magos locales van a estudiar sus hechizos. A la torre de marfil sólo se puede acceder a través de un proxy que asegura que sólo los tres primeros magos pueden entrar. Aquí el proxy representa la funcionalidad de la torre y le añade control de acceso.
|
||||
|
||||
En palabras llanas
|
||||
|
||||
> Usando el patrón proxy, una clase representa la funcionalidad de otra clase.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> Un proxy, en su forma más general, es una clase que funciona como interfaz de otra cosa. Un proxy es una envoltura o un objeto agente que está siendo llamado por el cliente para acceder al objeto real de servicio detrás de las escenas. El uso del proxy puede ser simplemente el reenvío al objeto real, o puede proporcionar lógica adicional. En el proxy se puede proporcionar funcionalidad adicional, por ejemplo, almacenamiento en caché cuando las operaciones en el objeto real consumen muchos recursos, o comprobación de condiciones previas antes de invocar operaciones en el objeto real.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tomando el ejemplo anterior de nuestra torre de asistentes. En primer lugar tenemos la interfaz `WizardTower` y la clase `IvoryTower`.
|
||||
|
||||
```java
|
||||
public interface WizardTower {
|
||||
|
||||
void enter(Wizard wizard);
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
public class IvoryTower implements WizardTower {
|
||||
|
||||
public void enter(Wizard wizard) {
|
||||
LOGGER.info("{} enters the tower.", wizard);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
A continuación, una simple clase `Wizard`.
|
||||
|
||||
```java
|
||||
public class Wizard {
|
||||
|
||||
private final String name;
|
||||
|
||||
public Wizard(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Luego tenemos el `WizardTowerProxy` para añadir control de acceso a `WizardTower`.
|
||||
|
||||
```java
|
||||
@Slf4j
|
||||
public class WizardTowerProxy implements WizardTower {
|
||||
|
||||
private static final int NUM_WIZARDS_ALLOWED = 3;
|
||||
|
||||
private int numWizards;
|
||||
|
||||
private final WizardTower tower;
|
||||
|
||||
public WizardTowerProxy(WizardTower tower) {
|
||||
this.tower = tower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enter(Wizard wizard) {
|
||||
if (numWizards < NUM_WIZARDS_ALLOWED) {
|
||||
tower.enter(wizard);
|
||||
numWizards++;
|
||||
} else {
|
||||
LOGGER.info("{} is not allowed to enter!", wizard);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Y aquí está el escenario de entrada a la torre.
|
||||
|
||||
```java
|
||||
var proxy = new WizardTowerProxy(new IvoryTower());
|
||||
proxy.enter(new Wizard("Red wizard"));
|
||||
proxy.enter(new Wizard("White wizard"));
|
||||
proxy.enter(new Wizard("Black wizard"));
|
||||
proxy.enter(new Wizard("Green wizard"));
|
||||
proxy.enter(new Wizard("Brown wizard"));
|
||||
```
|
||||
|
||||
Salida del programa:
|
||||
|
||||
```
|
||||
Red wizard enters the tower.
|
||||
White wizard enters the tower.
|
||||
Black wizard enters the tower.
|
||||
Green wizard is not allowed to enter!
|
||||
Brown wizard is not allowed to enter!
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
El proxy es aplicable siempre que se necesite una referencia a un objeto más versátil o sofisticada que un simple puntero.
|
||||
que un simple puntero. He aquí varias situaciones comunes en las que el patrón Proxy es
|
||||
aplicable.
|
||||
|
||||
* Un proxy remoto proporciona un representante local para un objeto en un espacio de direcciones diferente.
|
||||
* El proxy virtual crea objetos caros bajo demanda.
|
||||
* Un proxy de protección controla el acceso al objeto original. Los proxies de protección son útiles cuando
|
||||
objetos deben tener diferentes derechos de acceso.
|
||||
|
||||
Típicamente, el patrón proxy se utiliza para
|
||||
|
||||
* Controlar el acceso a otro objeto
|
||||
* Inicialización perezosa
|
||||
* Implementar el registro
|
||||
* Facilitar la conexión de red
|
||||
* Contar referencias a un objeto
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Controlling Access With Proxy Pattern](http://java-design-patterns.com/blog/controlling-access-with-proxy-pattern/)
|
||||
|
||||
## Usos conocidos
|
||||
|
||||
* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html)
|
||||
* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/)
|
||||
* Mocking frameworks [Mockito](https://site.mockito.org/),
|
||||
[Powermock](https://powermock.github.io/), [EasyMock](https://easymock.org/)
|
||||
* [UIAppearance](https://developer.apple.com/documentation/uikit/uiappearance)
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* [Ambassador](https://java-design-patterns.com/patterns/ambassador/)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
|
||||
* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
|
||||
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Role Object
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## También conocido como
|
||||
Patrón Post, Patrón Extension Object
|
||||
|
||||
## Propósito
|
||||
Adaptar un objeto a las necesidades de diferentes clientes mediante objetos de rol adjuntos de forma transparente, cada uno de los cuales representa un papel
|
||||
que el objeto debe desempeñar en el contexto de ese cliente. El objeto gestiona su conjunto de roles de forma dinámica. Al representar los roles como
|
||||
objetos individuales, los distintos contextos se mantienen separados y se simplifica la configuración del sistema.
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utiliza el patrón Objeto Rol, si:
|
||||
|
||||
- Quieres manejar una abstracción clave en diferentes contextos y no quieres poner las interfaces específicas de contexto resultantes en la misma interfaz de clase.
|
||||
- Quieres manejar los roles disponibles dinámicamente para que puedan ser adjuntados y removidos bajo demanda, es decir en tiempo de ejecución, en lugar de fijarlos estáticamente en tiempo de compilación.
|
||||
- Quiere tratar las extensiones de forma transparente y necesita preservar la identidad lógica del objeto del conglomerado de objetos resultante.
|
||||
- Desea mantener los pares rol/cliente independientes entre sí, de modo que los cambios en un rol no afecten a los clientes que no estén interesados en ese rol.
|
||||
|
||||
## Créditos
|
||||
|
||||
- [Hillside - Role object pattern](https://hillside.net/plop/plop97/Proceedings/riehle.pdf)
|
||||
- [Role object](http://wiki.c2.com/?RoleObject)
|
||||
- [Fowler - Dealing with roles](https://martinfowler.com/apsupp/roles.pdf)
|
||||
|
After Width: | Height: | Size: 63 KiB |
@@ -0,0 +1,133 @@
|
||||
---
|
||||
title: Separated Interface
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Decoupling
|
||||
---
|
||||
|
||||
|
||||
## Propósito
|
||||
|
||||
Separe la definición de la interfaz y su implementación en paquetes diferentes. Esto permite al cliente desconozca por completo la implementación.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo del mundo real
|
||||
|
||||
> Se puede crear un generador de facturas con capacidad para utilizar diferentes calculadoras de impuestos que se pueden añadir en la factura en función del tipo de compra, región, etc.
|
||||
|
||||
En pocas palabras
|
||||
|
||||
> El patrón de interfaz separada anima a mantener las implementaciones de una interfaz desacopladas del cliente y su definición, para que el cliente no dependa de la implementación.
|
||||
|
||||
Un código cliente puede abstraer algunas funcionalidades específicas a una interfaz, y definir la definición de
|
||||
la interfaz como una SPI ([Service Programming Interface](https://en.wikipedia.org/wiki/Service_provider_interface)
|
||||
es una API pensada y abierta para ser implementada o ampliada por terceros). Otro paquete puede
|
||||
implementar esta definición de interfaz con una lógica concreta, que se inyectará en el código del cliente en tiempo de ejecución (con un tercero).
|
||||
cliente en tiempo de ejecución (con una tercera clase, inyectando la implementación en el cliente) o en tiempo de compilación
|
||||
(utilizando el patrón Plugin con algún archivo configurable).
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
**Cliente**
|
||||
|
||||
La clase `InvoiceGenerator` acepta el coste del producto y calcula el importe total a pagar, impuestos incluidos.
|
||||
total a pagar, impuestos incluidos.
|
||||
|
||||
```java
|
||||
public class InvoiceGenerator {
|
||||
|
||||
private final TaxCalculator taxCalculator;
|
||||
|
||||
private final double amount;
|
||||
|
||||
public InvoiceGenerator(double amount, TaxCalculator taxCalculator) {
|
||||
this.amount = amount;
|
||||
this.taxCalculator = taxCalculator;
|
||||
}
|
||||
|
||||
public double getAmountWithTax() {
|
||||
return amount + taxCalculator.calculate(amount);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
La lógica de cálculo de impuestos se delega en la interfaz `TaxCalculator`.
|
||||
|
||||
```java
|
||||
public interface TaxCalculator {
|
||||
|
||||
double calculate(double amount);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**Paquete de aplicación**
|
||||
|
||||
En otro paquete (que el cliente desconoce por completo) existen múltiples implementaciones
|
||||
de la interfaz `TaxCalculator`. Una de ellas es `ForeignTaxCalculator`, que aplica un impuesto del 60
|
||||
para los productos internacionales.
|
||||
|
||||
```java
|
||||
public class ForeignTaxCalculator implements TaxCalculator {
|
||||
|
||||
public static final double TAX_PERCENTAGE = 60;
|
||||
|
||||
@Override
|
||||
public double calculate(double amount) {
|
||||
return amount * TAX_PERCENTAGE / 100.0;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Otra es `DomesticTaxCalculator`, que grava con un 20% los productos internacionales.
|
||||
|
||||
```java
|
||||
public class DomesticTaxCalculator implements TaxCalculator {
|
||||
|
||||
public static final double TAX_PERCENTAGE = 20;
|
||||
|
||||
@Override
|
||||
public double calculate(double amount) {
|
||||
return amount * TAX_PERCENTAGE / 100.0;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Estas dos implementaciones son instanciadas e inyectadas en la clase cliente por la clase `App.java`.
|
||||
en la clase cliente.
|
||||
|
||||
```java
|
||||
var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST, new ForeignTaxCalculator());
|
||||
|
||||
LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax());
|
||||
|
||||
var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTaxCalculator());
|
||||
|
||||
LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax());
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón de interfaz separada cuando
|
||||
|
||||
* Estás desarrollando un paquete de framework, y tu framework necesita llamar a algún código de aplicación a través de interfaces.
|
||||
* Tienes paquetes separados que implementan las funcionalidades que pueden ser conectadas a tu código cliente en tiempo de ejecución o de compilación.
|
||||
* Su código reside en una capa a la que no se le permite llamar a la capa de implementación de la interfaz por norma. Por ejemplo, una capa de dominio necesita llamar a un mapeador de datos.
|
||||
|
||||
## Tutoriales
|
||||
|
||||
* [Separated Interface Tutorial](https://www.youtube.com/watch?v=d3k-hOA7k2Y)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Martin Fowler](https://www.martinfowler.com/eaaCatalog/separatedInterface.html)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=e08dfb7f2cf6153542ef1b5a00b10abc)
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: Single Table Inheritance Pattern
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Data access
|
||||
---
|
||||
|
||||
## Single Table Inheritance(STI)
|
||||
|
||||
## Propósito
|
||||
|
||||
Representa una jerarquía hereditaria de clases como una única tabla que tiene columnas para todos los campos de las distintas clases.
|
||||
|
||||
## Explicación
|
||||
|
||||
Un ejemplo real
|
||||
|
||||
> Puede haber muchos tipos diferentes de vehículos en este mundo pero todos ellos se engloban bajo el único paraguas de Vehículo
|
||||
|
||||
En palabras llanas
|
||||
|
||||
> Mapea cada instancia de clase de un árbol de herencia en una única tabla.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> La herencia de tabla única es una forma de emular la herencia orientada a objetos en una base de datos relacional. Al mapear desde una tabla de la base de datos a un objeto en un lenguaje orientado a objetos, un campo de la base de datos identifica a qué clase de la jerarquía pertenece el objeto. Todos los campos de todas las clases se almacenan en la misma tabla, de ahí el nombre de "herencia de tabla única".
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Baeldung - Herencia de Hibernate
|
||||
|
||||
> Podemos definir la estrategia que queremos utilizar añadiendo la anotación @Inheritance a la superclase:
|
||||
|
||||
```java
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
public class MyProduct {
|
||||
@Id
|
||||
private long productId;
|
||||
private String name;
|
||||
|
||||
// constructor, getters, setters
|
||||
}
|
||||
```
|
||||
|
||||
El identificador de las entidades también se define en la superclase.
|
||||
|
||||
A continuación, podemos añadir las entidades de la subclase:
|
||||
|
||||
```java
|
||||
@Entity
|
||||
public class Book extends MyProduct {
|
||||
private String author;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Entity
|
||||
public class Pen extends MyProduct {
|
||||
private String color;
|
||||
}
|
||||
```
|
||||
Valores del discriminador
|
||||
|
||||
- Dado que los registros de todas las entidades estarán en la misma tabla, Hibernate necesita una forma de diferenciarlos.
|
||||
|
||||
- Por defecto, esto se hace a través de una columna discriminadora llamada DTYPE que tiene como valor el nombre de la entidad.
|
||||
|
||||
- Para personalizar la columna discriminadora, podemos utilizar la anotación @DiscriminatorColumn:
|
||||
|
||||
```java
|
||||
@Entity(name="products")
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
@DiscriminatorColumn(name="product_type",
|
||||
discriminatorType = DiscriminatorType.INTEGER)
|
||||
public class MyProduct {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- Aquí hemos optado por diferenciar las entidades de la subclase MyProduct mediante una columna entera llamada product_type.
|
||||
|
||||
- A continuación, tenemos que indicar a Hibernate qué valor tendrá cada registro de subclase para la columna product_type:
|
||||
|
||||
```java
|
||||
@Entity
|
||||
@DiscriminatorValue("1")
|
||||
public class Book extends MyProduct {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
```java
|
||||
@Entity
|
||||
@DiscriminatorValue("2")
|
||||
public class Pen extends MyProduct {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
- Hibernate añade otros dos valores predefinidos que puede tomar la anotación: null y not null:
|
||||
|
||||
- @DiscriminatorValue("null") significa que cualquier fila sin valor discriminador se asignará a la clase de entidad con esta anotación; esto puede aplicarse a la clase raíz de la jerarquía.
|
||||
- @DiscriminatorValue("not null"): cualquier fila con un valor discriminador que no coincida con ninguno de los asociados a las definiciones de entidad se asignará a la clase con esta anotación.
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón Singleton cuando
|
||||
|
||||
## Use STI Cuando Las Subclases Tienen Los Mismos Campos/Columnas Pero Diferente Comportamiento
|
||||
- Una buena indicación de que STI es correcto es cuando las diferentes subclases tienen los mismos campos/columnas pero diferentes métodos. En el ejemplo de cuentas anterior, esperamos que todas las columnas de la base de datos sean utilizadas por cada subclase. De lo contrario, habrá muchas columnas nulas en la base de datos.
|
||||
<br><br>
|
||||
* Utilizar STI cuando esperamos realizar consultas en todas las subclases
|
||||
- Otra buena indicación de que STI es correcto es si esperamos realizar consultas a través de todas las clases. Por ejemplo, si queremos encontrar las 10 cuentas con los saldos más altos en todas las clases, STI nos permite utilizar sólo una consulta, mientras que MTI requerirá manipulación en memoria.
|
||||
|
||||
### Tutoriales
|
||||
|
||||
- <a href ="https://www.youtube.com/watch?v=M5YrLtAHtOo" >Cerebros de Java - Herencia de tabla única</a>
|
||||
|
||||
## Consecuencias
|
||||
|
||||
* Los campos a veces son relevantes y a veces no, lo que puede confundir a la gente que usa las tablas directamente.
|
||||
* Las columnas utilizadas sólo por algunas subclases hacen que se desperdicie espacio en la base de datos.
|
||||
Hasta qué punto esto es realmente un problema depende de las características
|
||||
específicas de los datos y de lo bien que la base de datos comprima las columnas vacías.
|
||||
Oracle, por ejemplo, es muy eficiente en el recorte de espacio desperdiciado, particularmente columnas opcionales a la derecha de la tabla de la base de datos.
|
||||
Cada base de datos tiene sus propios trucos para esto.
|
||||
* La tabla única puede acabar siendo demasiado grande, con muchos índices y frecuentes, lo que puede perjudicar el rendimiento. Puede evitar esto teniendo
|
||||
tablas de índices separadas que o bien enumeran claves de filas que tienen una determinada propiedad o que copien un subconjunto de campos relevantes para un índice.
|
||||
* Sólo se dispone de un único espacio de nombres para los campos, por lo que hay que asegurarse de que
|
||||
de no utilizar el mismo nombre para diferentes campos. Los nombres compuestos con el nombre de la clase como prefijo o sufijo.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
* MappedSuperclass
|
||||
* Single Table
|
||||
* Joined Table
|
||||
* Table per Class
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Single Table Inheritance - martinFowler.com](https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html)
|
||||
* [Patterns of Enterprise Application Architecture](https://books.google.co.in/books?id=vqTfNFDzzdIC&pg=PA278&redir_esc=y#v=onepage&q&f=false)
|
||||
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Strangler
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Extensibility
|
||||
- Cloud distributed
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Migrar de forma incremental un sistema heredado sustituyendo gradualmente piezas específicas de funcionalidad
|
||||
con nuevas aplicaciones y servicios. A medida que se sustituyen las funciones del sistema heredado, el nuevo
|
||||
sistema acaba cubriendo todas las funciones del sistema antiguo y puede tener sus propias funciones nuevas, con lo que
|
||||
estrangulando el sistema antiguo y permitiéndole retirarlo del servicio.
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Este patrón estrangulador es una manera segura de eliminar gradualmente una cosa por algo mejor, más barato, o
|
||||
más expandible. Especialmente cuando se quiere actualizar el sistema heredado con nuevas técnicas y se necesita
|
||||
desarrollar continuamente nuevas características al mismo tiempo. Tenga en cuenta que este patrón requiere un esfuerzo adicional,
|
||||
por lo que normalmente se utiliza cuando el sistema no es tan simple.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Strangler pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler)
|
||||
* [Legacy Application Strangulation : Case Studies](https://paulhammant.com/2013/07/14/legacy-application-strangulation-case-studies/)
|
||||
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,133 @@
|
||||
---
|
||||
title: Table Module
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Data access
|
||||
---
|
||||
|
||||
## Propósito
|
||||
El módulo de tablas organiza la lógica del dominio con una clase por tabla de la base de datos, y una única instancia de una clase contiene los distintos procedimientos que actuarán sobre los datos.
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo del mundo real
|
||||
|
||||
> Cuando tratamos con un sistema de usuarios, necesitamos algunas operaciones sobre la tabla de usuarios. Podemos utilizar el patrón de módulo de tabla en este escenario. Podemos crear una clase llamada UserTableModule e inicializar una instancia de esa clase para manejar la lógica de negocio para todas las filas de la tabla de usuarios.
|
||||
|
||||
En palabras simples
|
||||
|
||||
> Una única instancia que maneja la lógica de negocio para todas las filas de una tabla o vista de la base de datos.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
En el ejemplo del sistema de usuarios, necesitamos manejar la lógica de dominio del login y registro de usuarios. Podemos utilizar el patrón de módulo de tabla y crear una instancia de la clase `UserTableModule` para manejar la lógica de negocio de todas las filas de la tabla de usuarios.
|
||||
|
||||
Aquí está la entidad básica `User`.
|
||||
|
||||
```java
|
||||
@Setter
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
public class User {
|
||||
private int id;
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
||||
```
|
||||
|
||||
Aquí está la clase `UserTableModule`.
|
||||
|
||||
```java
|
||||
public class UserTableModule {
|
||||
private final DataSource dataSource;
|
||||
private Connection connection = null;
|
||||
private ResultSet resultSet = null;
|
||||
private PreparedStatement preparedStatement = null;
|
||||
|
||||
public UserTableModule(final DataSource userDataSource) {
|
||||
this.dataSource = userDataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login using username and password.
|
||||
*
|
||||
* @param username the username of a user
|
||||
* @param password the password of a user
|
||||
* @return the execution result of the method
|
||||
* @throws SQLException if any error
|
||||
*/
|
||||
public int login(final String username, final String password) throws SQLException {
|
||||
// Method implementation.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new user.
|
||||
*
|
||||
* @param user a user instance
|
||||
* @return the execution result of the method
|
||||
* @throws SQLException if any error
|
||||
*/
|
||||
public int registerUser(final User user) throws SQLException {
|
||||
// Method implementation.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
En la clase `App`, usamos una instancia del `UserTableModule` para manejar el login y registro de usuarios.
|
||||
|
||||
```java
|
||||
// Create data source and create the user table.
|
||||
final var dataSource = createDataSource();
|
||||
createSchema(dataSource);
|
||||
userTableModule = new UserTableModule(dataSource);
|
||||
|
||||
//Initialize two users.
|
||||
var user1 = new User(1, "123456", "123456");
|
||||
var user2 = new User(2, "test", "password");
|
||||
|
||||
//Login and register using the instance of userTableModule.
|
||||
userTableModule.registerUser(user1);
|
||||
userTableModule.login(user1.getUsername(), user1.getPassword());
|
||||
userTableModule.login(user2.getUsername(), user2.getPassword());
|
||||
userTableModule.registerUser(user2);
|
||||
userTableModule.login(user2.getUsername(), user2.getPassword());
|
||||
|
||||
deleteSchema(dataSource);
|
||||
```
|
||||
|
||||
La salida del programa:
|
||||
|
||||
```java
|
||||
12:22:13.095 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully!
|
||||
12:22:13.117 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully!
|
||||
12:22:13.128 [main] INFO com.iluwatar.tablemodule.UserTableModule - Fail to login!
|
||||
12:22:13.136 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully!
|
||||
12:22:13.144 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully!
|
||||
```
|
||||
|
||||
## Diagrama de clases
|
||||
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
|
||||
Utilice el patrón de módulo de tabla cuando
|
||||
|
||||
- La lógica del dominio es simple y los datos están en forma tabular.
|
||||
- La aplicación sólo utiliza unas pocas estructuras de datos comunes compartidas orientadas a tablas.
|
||||
|
||||
## Patrones relacionados
|
||||
|
||||
- [Transaction Script](https://java-design-patterns.com/patterns/transaction-script/)
|
||||
|
||||
- [Domain Model](https://java-design-patterns.com/patterns/domain-model/)
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Table Module Pattern](http://wiki3.cosc.canterbury.ac.nz/index.php/Table_module_pattern)
|
||||
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=18acc13ba60d66690009505577c45c04)
|
||||
* [Architecture patterns: domain model and friends](https://inviqa.com/blog/architecture-patterns-domain-model-and-friends)
|
||||
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,156 @@
|
||||
---
|
||||
title: Twin
|
||||
category: Structural
|
||||
language: es
|
||||
tag:
|
||||
- Extensibility
|
||||
---
|
||||
|
||||
## Propósito
|
||||
Twin pattern es un patrón de diseño que proporciona una solución estándar para simular herencia múltiple en java
|
||||
|
||||
## Explicación
|
||||
|
||||
Ejemplo real
|
||||
|
||||
> Consideremos un juego con una pelota que necesita características de dos tipos, Game Item, e hilos para funcionar sin problemas en el juego. Podemos utilizar dos objetos, con un objeto compatible con el primer tipo y el otro compatible con el segundo tipo. El par de objetos juntos pueden funcionar como una pelota en el juego.
|
||||
|
||||
En palabras llanas
|
||||
|
||||
> Proporciona una forma de formar dos subclases estrechamente acopladas que pueden actuar como una clase gemela que tiene dos extremos.
|
||||
|
||||
Wikipedia dice
|
||||
|
||||
> En ingeniería de software, el patrón Gemelo es un patrón de diseño de software que permite a los desarrolladores modelar herencia múltiple en lenguajes de programación que no soportan herencia múltiple. Este patrón evita muchos de los problemas de la herencia múltiple.
|
||||
|
||||
**Ejemplo programático**
|
||||
|
||||
Tomemos nuestro ejemplo anterior de la bola de juego. Consideremos que tenemos un juego en el que la pelota necesita ser tanto un `GameItem` como un `Thread`.
|
||||
En primer lugar, tenemos la clase `GameItem` dada a continuación y la clase `Thread`.
|
||||
|
||||
|
||||
```java
|
||||
|
||||
@Slf4j
|
||||
public abstract class GameItem {
|
||||
|
||||
public void draw() {
|
||||
LOGGER.info("draw");
|
||||
doDraw();
|
||||
}
|
||||
|
||||
public abstract void doDraw();
|
||||
|
||||
|
||||
public abstract void click();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
A continuación, tenemos las subclases `BallItem` y `BallThread` que las heredan, respectivamente.
|
||||
|
||||
```java
|
||||
|
||||
@Slf4j
|
||||
public class BallItem extends GameItem {
|
||||
|
||||
private boolean isSuspended;
|
||||
|
||||
@Setter
|
||||
private BallThread twin;
|
||||
|
||||
@Override
|
||||
public void doDraw() {
|
||||
|
||||
LOGGER.info("doDraw");
|
||||
}
|
||||
|
||||
public void move() {
|
||||
LOGGER.info("move");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click() {
|
||||
|
||||
isSuspended = !isSuspended;
|
||||
|
||||
if (isSuspended) {
|
||||
twin.suspendMe();
|
||||
} else {
|
||||
twin.resumeMe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class BallThread extends Thread {
|
||||
|
||||
@Setter
|
||||
private BallItem twin;
|
||||
|
||||
private volatile boolean isSuspended;
|
||||
|
||||
private volatile boolean isRunning = true;
|
||||
|
||||
/**
|
||||
* Run the thread.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
while (isRunning) {
|
||||
if (!isSuspended) {
|
||||
twin.draw();
|
||||
twin.move();
|
||||
}
|
||||
try {
|
||||
Thread.sleep(250);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void suspendMe() {
|
||||
isSuspended = true;
|
||||
LOGGER.info("Begin to suspend BallThread");
|
||||
}
|
||||
|
||||
public void resumeMe() {
|
||||
isSuspended = false;
|
||||
LOGGER.info("Begin to resume BallThread");
|
||||
}
|
||||
|
||||
public void stopMe() {
|
||||
this.isRunning = false;
|
||||
this.isSuspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Ahora, cuando necesitemos la pelota, podemos instanciar objetos tanto del `BallThread` como del `BallItem` como un par y pasarlos a su objeto par para que puedan actuar juntos según convenga.
|
||||
|
||||
```java
|
||||
|
||||
var ballItem = new BallItem();
|
||||
var ballThread = new BallThread();
|
||||
|
||||
ballItem.setTwin(ballThread);
|
||||
ballThread.setTwin(ballItem);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Diagrama de clases
|
||||

|
||||
|
||||
## Aplicabilidad
|
||||
Utilice el lenguaje Twin
|
||||
|
||||
* Para simular herencia múltiple en un lenguaje que no soporta esta característica.
|
||||
* Para evitar ciertos problemas de la herencia múltiple como los choques de nombres.
|
||||
|
||||
## Créditos
|
||||
|
||||
* [Twin – A Design Pattern for Modeling Multiple Inheritance](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf)
|
||||
|
After Width: | Height: | Size: 29 KiB |