diff --git a/localization/es/servant/README.md b/localization/es/servant/README.md new file mode 100644 index 000000000..97b18456d --- /dev/null +++ b/localization/es/servant/README.md @@ -0,0 +1,235 @@ +--- +title: Servant +category: Behavioral +language: es +tag: +- Decoupling +--- + +## Propósito +Servant se utiliza para proporcionar algún comportamiento a un grupo de clases. +En lugar de definir ese comportamiento en cada clase - o cuando no podemos factorizar +este comportamiento en la clase padre común - se define una vez en el Servant. + +## Explicación + +Ejemplo del mundo real + +> El Rey, la Reina y otros miembros reales de palacio necesitan sirvientes que les den de comer, +> organizar bebidas, etc. + +En otras palabras + +> Garantiza que un objeto servidor preste determinados servicios a un grupo de clases atendidas. + +Wikipedia dice + +> En ingeniería de software, el patrón Servant define un objeto utilizado para ofrecer alguna funcionalidad +> a un grupo de clases sin definir esa funcionalidad en cada una de ellas. Un Servant es una clase +> cuya instancia (o incluso sólo clase) proporciona métodos que se encargan de un servicio deseado, mientras que +> los objetos para los que (o con los que) el Servant hace algo, se toman como parámetros. + +**Ejemplo programático** + +La clase Servant puede prestar servicios a otros miembros reales de palacio. + +```java +/** + * Servant. + */ +public class Servant { + + public String name; + + /** + * Constructor. + */ + public Servant(String name) { + this.name = name; + } + + public void feed(Royalty r) { + r.getFed(); + } + + public void giveWine(Royalty r) { + r.getDrink(); + } + + public void giveCompliments(Royalty r) { + r.receiveCompliments(); + } + + /** + * Check if we will be hanged. + */ + public boolean checkIfYouWillBeHanged(List tableGuests) { + return tableGuests.stream().allMatch(Royalty::getMood); + } +} +``` + +Royalty es una interfaz. Es implementada por las clases King, y Queen para obtener servicios de servant. + +```java +interface Royalty { + + void getFed(); + + void getDrink(); + + void changeMood(); + + void receiveCompliments(); + + boolean getMood(); +} +``` +La clase King implementa la interfaz Royalty. +```java +public class King implements Royalty { + + private boolean isDrunk; + private boolean isHungry = true; + private boolean isHappy; + private boolean complimentReceived; + + @Override + public void getFed() { + isHungry = false; + } + + @Override + public void getDrink() { + isDrunk = true; + } + + public void receiveCompliments() { + complimentReceived = true; + } + + @Override + public void changeMood() { + if (!isHungry && isDrunk) { + isHappy = true; + } + if (complimentReceived) { + isHappy = false; + } + } + + @Override + public boolean getMood() { + return isHappy; + } +} +``` +La clase Queen implementa la interfaz Royalty. +```java +public class Queen implements Royalty { + + private boolean isDrunk = true; + private boolean isHungry; + private boolean isHappy; + private boolean isFlirty = true; + private boolean complimentReceived; + + @Override + public void getFed() { + isHungry = false; + } + + @Override + public void getDrink() { + isDrunk = true; + } + + public void receiveCompliments() { + complimentReceived = true; + } + + @Override + public void changeMood() { + if (complimentReceived && isFlirty && isDrunk && !isHungry) { + isHappy = true; + } + } + + @Override + public boolean getMood() { + return isHappy; + } + + public void setFlirtiness(boolean f) { + this.isFlirty = f; + } + +} +``` + +Luego: + +```java +public class App { + + private static final Servant jenkins = new Servant("Jenkins"); + private static final Servant travis = new Servant("Travis"); + + /** + * Program entry point. + */ + public static void main(String[] args) { + scenario(jenkins, 1); + scenario(travis, 0); + } + + /** + * Can add a List with enum Actions for variable scenarios. + */ + public static void scenario(Servant servant, int compliment) { + var k = new King(); + var q = new Queen(); + + var guests = List.of(k, q); + + // feed + servant.feed(k); + servant.feed(q); + // serve drinks + servant.giveWine(k); + servant.giveWine(q); + // compliment + servant.giveCompliments(guests.get(compliment)); + + // outcome of the night + guests.forEach(Royalty::changeMood); + + // check your luck + if (servant.checkIfYouWillBeHanged(guests)) { + LOGGER.info("{} will live another day", servant.name); + } else { + LOGGER.info("Poor {}. His days are numbered", servant.name); + } + } +} +``` + +La salida de la consola + +``` +Jenkins will live another day +Poor Travis. His days are numbered +``` + + +## Diagrama de clases +![alt text](./etc/servant-pattern.png "Servant") + +## Aplicabilidad +Utiliza el patrón Servant cuando + +* Cuando queremos que algunos objetos realicen una acción común y no queremos definir esta acción como un método en cada clase. + +## Créditos + +* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) diff --git a/localization/es/servant/etc/servant-pattern.png b/localization/es/servant/etc/servant-pattern.png new file mode 100644 index 000000000..a8237775e Binary files /dev/null and b/localization/es/servant/etc/servant-pattern.png differ diff --git a/localization/es/sharding/README.md b/localization/es/sharding/README.md new file mode 100644 index 000000000..36cfa424f --- /dev/null +++ b/localization/es/sharding/README.md @@ -0,0 +1,27 @@ +--- +title: Sharding +category: Behavioral +language: es +tag: + - Performance + - Cloud distributed +--- + +## Propósito +El patrón sharding significa dividir el almacén de datos en particiones horizontales o shards. Cada fragmento tiene el mismo esquema, pero contiene su propio subconjunto de datos. +Un fragmento es un almacén de datos en sí mismo (puede contener los datos de muchas entidades de diferentes tipos), que se ejecuta en un servidor que actúa como nodo de almacenamiento. + +## Diagrama de clases +![alt text](./etc/sharding.urm.png "Sharding pattern class diagram") + +## Aplicabilidad +Este patrón ofrece las siguientes ventajas: + +- Se puede ampliar el sistema añadiendo más fragmentos que se ejecuten en nodos de almacenamiento adicionales. +- El sistema puede utilizar hardware comercial en lugar de ordenadores especializados (y caros) para cada nodo de almacenamiento. +- Se puede reducir la contención y mejorar el rendimiento equilibrando la carga de trabajo entre los shards. +- En la nube, los shards pueden ubicarse físicamente cerca de los usuarios que accederán a los datos. + +## Créditos + +* [Sharding pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/sharding) \ No newline at end of file diff --git a/localization/es/sharding/etc/sharding.urm.png b/localization/es/sharding/etc/sharding.urm.png new file mode 100644 index 000000000..e7f412af3 Binary files /dev/null and b/localization/es/sharding/etc/sharding.urm.png differ diff --git a/localization/es/spatial-partition/README.md b/localization/es/spatial-partition/README.md new file mode 100644 index 000000000..c88c9f2b2 --- /dev/null +++ b/localization/es/spatial-partition/README.md @@ -0,0 +1,73 @@ +--- +title: Spatial Partition +category: Behavioral +language: es +tag: + - Performance + - Game programming +--- + +## Propósito + +Como se explica en el libro [Game Programming Patterns](http://gameprogrammingpatterns.com/spatial-partition.html) +por Bob Nystrom, spatial partition pattern ayuda a localizar eficazmente los objetos almacenándolos en una +estructura de datos organizada por sus posiciones. + +## Explicación + +Digamos que usted está construyendo un juego de guerra con cientos, o incluso miles de jugadores, que se enfrentan en el campo de batalla. +La posición de cada jugador se actualiza en cada fotograma. La forma más sencilla de manejar +todas las interacciones que tienen lugar en el campo es comprobar la posición de cada jugador con la de todos los demás. +posición de cada jugador: + +```java +public void handleMeLee(Unit units[], int numUnits) { + for (var a = 0; a < numUnits - 1; a++) + { + for (var b = a + 1; b < numUnits; b++) + { + if (units[a].position() == units[b].position()) + { + handleAttack(units[a], units[b]); + } + } + } +} +``` + +Esto incluirá un montón de controles innecesarios entre jugadores que están demasiado alejados como para tener alguna +influencia entre ellos. Los bucles anidados dan a esta operación una complejidad O(n^2), que tiene que ser +cada fotograma, ya que muchos de los objetos del campo pueden moverse en cada fotograma. La idea +detrás del patrón de diseño de Partición Espacial es permitir una rápida localización de los objetos utilizando una estructura de datos que está organizada por sus posiciones. +que está organizada por sus posiciones, de modo que cuando se realiza una operación como la anterior, +no es necesario cotejar la posición de cada objeto con la de todos los demás. La estructura de datos +puede utilizarse para almacenar objetos en movimiento y estáticos, aunque para seguir la pista de los objetos en movimiento, +sus posiciones tendrán que restablecerse cada vez que se muevan. Esto significaría tener que crear una nueva +instancia de la estructura de datos cada vez que un objeto se mueva, lo que consumiría memoria adicional. La dirección +estructuras de datos comunes utilizadas para este patrón de diseño son: + +* Grid +* Árbol Quad +* Árbol K-d +* BSP +* Jerarquía de volúmenes límite + +En nuestra implementación, utilizamos la estructura de datos Quadtree que reducirá la complejidad temporal de +O(n^2) a O(nlogn), lo que reduce significativamente los cálculos necesarios en el caso de un gran número de objetos. +significativamente en el caso de un gran número de objetos. + +## Diagrama de clases + +![alt text](./etc/spatial-partition.urm.png "Spatial Partition pattern class diagram") + +## Aplicabilidad + +Utilice el patrón Spatial Partition cuando: + +* Cuando se necesita mantener un registro de un gran número de posiciones de objetos, que se actualizan cada fotograma. +* Cuando es aceptable cambiar memoria por velocidad, ya que la creación y actualización de la estructura de datos consumirá memoria extra. + +## Créditos + +* [Game Programming Patterns/Spatial Partition](http://gameprogrammingpatterns.com/spatial-partition.html) por Bob Nystrom +* [Quadtree tutorial](https://www.youtube.com/watch?v=OJxEcs0w_kE) por Daniel Schiffman diff --git a/localization/es/spatial-partition/etc/spatial-partition.urm.png b/localization/es/spatial-partition/etc/spatial-partition.urm.png new file mode 100644 index 000000000..5172bdb36 Binary files /dev/null and b/localization/es/spatial-partition/etc/spatial-partition.urm.png differ diff --git a/localization/es/special-case/README.md b/localization/es/special-case/README.md new file mode 100644 index 000000000..36d827795 --- /dev/null +++ b/localization/es/special-case/README.md @@ -0,0 +1,366 @@ +--- +title: Special Case +category: Behavioral +language: es +tag: + - Extensibility +--- + +## Propósito + +Define algunos casos especiales, y los encapsula en subclases que proporcionan diferentes comportamientos especiales. + +## Explicación + +Ejemplo del mundo real + +> En un sistema de comercio electrónico, la capa de presentación espera que la capa de aplicación produzca cierto modelo de vista. +> Tenemos un escenario de éxito, en el que el modelo de vista de recibo contiene datos reales de la compra, +> y un par de escenarios de fracaso. + +En otras palabras + +> El patrón Special Case permite devolver objetos reales no nulos que realizan comportamientos especiales. + +En [Patterns of Enterprise Application Architecture](https://martinfowler.com/books/eaa.html) se dice +la diferencia con el Patrón de Objeto Nulo + +> Si me perdonas el juego de palabras irresistible, yo veo el Objeto Nulo como un caso especial de Caso Especial. + +**Ejemplo programático** + +Para centrarnos en el patrón en sí, implementamos la BD y el bloqueo de mantenimiento del sistema de comercio electrónico mediante la instancia singleton. + +```java +public class Db { + private static Db instance; + private Map userName2User; + private Map user2Account; + private Map itemName2Product; + + public static Db getInstance() { + if (instance == null) { + synchronized (Db.class) { + if (instance == null) { + instance = new Db(); + instance.userName2User = new HashMap<>(); + instance.user2Account = new HashMap<>(); + instance.itemName2Product = new HashMap<>(); + } + } + } + return instance; + } + + public void seedUser(String userName, Double amount) { + User user = new User(userName); + instance.userName2User.put(userName, user); + Account account = new Account(amount); + instance.user2Account.put(user, account); + } + + public void seedItem(String itemName, Double price) { + Product item = new Product(price); + itemName2Product.put(itemName, item); + } + + public User findUserByUserName(String userName) { + if (!userName2User.containsKey(userName)) { + return null; + } + return userName2User.get(userName); + } + + public Account findAccountByUser(User user) { + if (!user2Account.containsKey(user)) { + return null; + } + return user2Account.get(user); + } + + public Product findProductByItemName(String itemName) { + if (!itemName2Product.containsKey(itemName)) { + return null; + } + return itemName2Product.get(itemName); + } + + public class User { + private String userName; + + public User(String userName) { + this.userName = userName; + } + + public String getUserName() { + return userName; + } + + public ReceiptDto purchase(Product item) { + return new ReceiptDto(item.getPrice()); + } + } + + public class Account { + private Double amount; + + public Account(Double amount) { + this.amount = amount; + } + + public MoneyTransaction withdraw(Double price) { + if (price > amount) { + return null; + } + return new MoneyTransaction(amount, price); + } + + public Double getAmount() { + return amount; + } + } + + public class Product { + private Double price; + + public Product(Double price) { + this.price = price; + } + + public Double getPrice() { + return price; + } + } +} + +public class MaintenanceLock { + private static final Logger LOGGER = LoggerFactory.getLogger(MaintenanceLock.class); + + private static MaintenanceLock instance; + private boolean lock = true; + + public static MaintenanceLock getInstance() { + if (instance == null) { + synchronized (MaintenanceLock.class) { + if (instance == null) { + instance = new MaintenanceLock(); + } + } + } + return instance; + } + + public boolean isLock() { + return lock; + } + + public void setLock(boolean lock) { + this.lock = lock; + LOGGER.info("Maintenance lock is set to: " + lock); + } +} +``` + +Primero presentaremos la capa de presentación, la interfaz del modelo de vista de recibo y su implementación de un escenario exitoso. + +```java +public interface ReceiptViewModel { + void show(); +} + +public class ReceiptDto implements ReceiptViewModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReceiptDto.class); + + private Double price; + + public ReceiptDto(Double price) { + this.price = price; + } + + public Double getPrice() { + return price; + } + + @Override + public void show() { + LOGGER.info("Receipt: " + price + " paid"); + } +} +``` + +Y aquí están las implementaciones de los escenarios de fracaso, que son los casos especiales. + +```java +public class DownForMaintenance implements ReceiptViewModel { + private static final Logger LOGGER = LoggerFactory.getLogger(DownForMaintenance.class); + + @Override + public void show() { + LOGGER.info("Down for maintenance"); + } +} + +public class InvalidUser implements ReceiptViewModel { + private static final Logger LOGGER = LoggerFactory.getLogger(InvalidUser.class); + + private final String userName; + + public InvalidUser(String userName) { + this.userName = userName; + } + + @Override + public void show() { + LOGGER.info("Invalid user: " + userName); + } +} + +public class OutOfStock implements ReceiptViewModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(OutOfStock.class); + + private String userName; + private String itemName; + + public OutOfStock(String userName, String itemName) { + this.userName = userName; + this.itemName = itemName; + } + + @Override + public void show() { + LOGGER.info("Out of stock: " + itemName + " for user = " + userName + " to buy"); + } +} + +public class InsufficientFunds implements ReceiptViewModel { + private static final Logger LOGGER = LoggerFactory.getLogger(InsufficientFunds.class); + + private String userName; + private Double amount; + private String itemName; + + public InsufficientFunds(String userName, Double amount, String itemName) { + this.userName = userName; + this.amount = amount; + this.itemName = itemName; + } + + @Override + public void show() { + LOGGER.info("Insufficient funds: " + amount + " of user: " + userName + + " for buying item: " + itemName); + } +} +``` + +En segundo lugar, está la capa de aplicación, la implementación de los servicios de aplicación y la implementación de los servicios de dominio. + +```java +public class ApplicationServicesImpl implements ApplicationServices { + private DomainServicesImpl domain = new DomainServicesImpl(); + + @Override + public ReceiptViewModel loggedInUserPurchase(String userName, String itemName) { + if (isDownForMaintenance()) { + return new DownForMaintenance(); + } + return this.domain.purchase(userName, itemName); + } + + private boolean isDownForMaintenance() { + return MaintenanceLock.getInstance().isLock(); + } +} + +public class DomainServicesImpl implements DomainServices { + public ReceiptViewModel purchase(String userName, String itemName) { + Db.User user = Db.getInstance().findUserByUserName(userName); + if (user == null) { + return new InvalidUser(userName); + } + + Db.Account account = Db.getInstance().findAccountByUser(user); + return purchase(user, account, itemName); + } + + private ReceiptViewModel purchase(Db.User user, Db.Account account, String itemName) { + Db.Product item = Db.getInstance().findProductByItemName(itemName); + if (item == null) { + return new OutOfStock(user.getUserName(), itemName); + } + + ReceiptDto receipt = user.purchase(item); + MoneyTransaction transaction = account.withdraw(receipt.getPrice()); + if (transaction == null) { + return new InsufficientFunds(user.getUserName(), account.getAmount(), itemName); + } + + return receipt; + } +} +``` + +Por último, el cliente envía solicitudes a los servicios de la aplicación para obtener la vista de la presentación. + +```java + // DB seeding + LOGGER.info("Db seeding: " + "1 user: {\"ignite1771\", amount = 1000.0}, " + + "2 products: {\"computer\": price = 800.0, \"car\": price = 20000.0}"); + Db.getInstance().seedUser("ignite1771", 1000.0); + Db.getInstance().seedItem("computer", 800.0); + Db.getInstance().seedItem("car", 20000.0); + + var applicationServices = new ApplicationServicesImpl(); + ReceiptViewModel receipt; + + LOGGER.info("[REQUEST] User: " + "abc123" + " buy product: " + "tv"); + receipt = applicationServices.loggedInUserPurchase("abc123", "tv"); + receipt.show(); + MaintenanceLock.getInstance().setLock(false); + LOGGER.info("[REQUEST] User: " + "abc123" + " buy product: " + "tv"); + receipt = applicationServices.loggedInUserPurchase("abc123", "tv"); + receipt.show(); + LOGGER.info("[REQUEST] User: " + "ignite1771" + " buy product: " + "tv"); + receipt = applicationServices.loggedInUserPurchase("ignite1771", "tv"); + receipt.show(); + LOGGER.info("[REQUEST] User: " + "ignite1771" + " buy product: " + "car"); + receipt = applicationServices.loggedInUserPurchase("ignite1771", "car"); + receipt.show(); + LOGGER.info("[REQUEST] User: " + "ignite1771" + " buy product: " + "computer"); + receipt = applicationServices.loggedInUserPurchase("ignite1771", "computer"); + receipt.show(); +``` + +Salida del programa de cada solicitud: + +``` + Down for maintenance + Invalid user: abc123 + Out of stock: tv for user = ignite1771 to buy + Insufficient funds: 1000.0 of user: ignite1771 for buying item: car + Receipt: 800.0 paid +``` + +## Diagrama de clases + +![alt text](./etc/special_case_urm.png "Special Case") + +## Aplicabilidad + +Utilice el patrón Special Case cuando: + +* Tienes varios lugares en el sistema que tienen el mismo comportamiento después de una comprobación condicional + para una instancia de clase en particular, o el mismo comportamiento después de una comprobación nula. +* Devuelve un objeto real que realiza el comportamiento real, en lugar de un objeto nulo que no realiza nada. + +## Tutorial + +* [Special Case Tutorial](https://www.codinghelmet.com/articles/reduce-cyclomatic-complexity-special-case) + +## Créditos + +* [How to Reduce Cyclomatic Complexity Part 2: Special Case Pattern](https://www.codinghelmet.com/articles/reduce-cyclomatic-complexity-special-case) +* [Patterns of Enterprise Application Architecture](https://martinfowler.com/books/eaa.html) +* [Special Case](https://www.martinfowler.com/eaaCatalog/specialCase.html) \ No newline at end of file diff --git a/localization/es/special-case/etc/special_case_urm.png b/localization/es/special-case/etc/special_case_urm.png new file mode 100644 index 000000000..03ca646f3 Binary files /dev/null and b/localization/es/special-case/etc/special_case_urm.png differ diff --git a/localization/es/specification/README.md b/localization/es/specification/README.md new file mode 100644 index 000000000..705dfbeba --- /dev/null +++ b/localization/es/specification/README.md @@ -0,0 +1,213 @@ +--- +title: Specification +category: Behavioral +language: es +tag: + - Data access +--- + +## También conocido como + +Filter, Criteria + +## Propósito + +El patrón de Specification separa la declaración de cómo emparejar un candidato, del objeto candidato +con el que se compara. Además de su utilidad en la selección, también es valioso para la +validación y para la construcción por encargo. + +## Explicación + +Ejemplo del mundo real + +> Hay un conjunto de criaturas diferentes y a menudo necesitamos seleccionar algún subconjunto de ellas. Podemos +> escribir nuestra especificación de búsqueda como "criaturas que puedan volar", "criaturas de más de 500 +> kilogramos", o como una combinación de otras especificaciones de búsqueda. +> que realizará el filtrado. + +En palabras sencillas + +> El patrón de especificación nos permite separar los criterios de búsqueda del objeto que realiza la +> búsqueda. + +Wikipedia dice + +> En programación informática, el patrón de especificación es un patrón particular de diseño de software, +> por el cual las reglas de negocio pueden ser recombinadas encadenando las reglas de negocio usando lógica booleana +> lógica booleana. + +**Ejemplo programático** + +Si nos fijamos en nuestro ejemplo anterior, tenemos un conjunto de criaturas con ciertas propiedades. +propiedades. Esas propiedades pueden formar parte de un conjunto predefinido y limitado (representado aquí por los +enums Tamaño, Movimiento y Color); pero también pueden ser valores continuos (por ejemplo, la masa de una +criatura). En este caso, es más apropiado utilizar lo que llamamos "especificación parametrizada", +en la que el valor de la propiedad puede darse como argumento al instanciar la criatura, lo que permite +mayor flexibilidad. Una tercera opción es combinar propiedades predefinidas y/o parametrizadas utilizando +lógica booleana, lo que permite posibilidades de selección casi ilimitadas (esto se denomina "especificación compuesta", véase más adelante). +especificación compuesta", véase más adelante). Los pros y los contras de cada enfoque se detallan en la tabla al final de este documento. +de este documento. + +```java +public interface Creature { + String getName(); + Size getSize(); + Movement getMovement(); + Color getColor(); + Mass getMass(); +} +``` + +Y la implementación de `Dragon` tiene este aspecto. + +```java +public class Dragon extends AbstractCreature { + + public Dragon() { + super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0)); + } +} +``` + +Ahora que queremos seleccionar algún subconjunto de ellos, utilizamos selectores. Para seleccionar criaturas que vuelan +debemos usar `MovementSelector`. + +```java +public class MovementSelector extends AbstractSelector { + + private final Movement movement; + + public MovementSelector(Movement m) { + this.movement = m; + } + + @Override + public boolean test(Creature t) { + return t.getMovement().equals(movement); + } +} +``` + +Por otro lado, cuando seleccionamos criaturas más pesadas que una cantidad elegida, usamos +`MassGreaterThanSelector`. + +```java +public class MassGreaterThanSelector extends AbstractSelector { + + private final Mass mass; + + public MassGreaterThanSelector(double mass) { + this.mass = new Mass(mass); + } + + @Override + public boolean test(Creature t) { + return t.getMass().greaterThan(mass); + } +} +``` + +Con estos elementos en su lugar, podemos realizar una búsqueda de criaturas rojas de la siguiente manera: + +```java + var redCreatures = creatures.stream().filter(new ColorSelector(Color.RED)) + .collect(Collectors.toList()); +``` + +Pero también podríamos usar nuestro selector parametrizado así: + +```java + var heavyCreatures = creatures.stream().filter(new MassGreaterThanSelector(500.0) + .collect(Collectors.toList()); +``` + +Nuestra tercera opción es combinar varios selectores. La búsqueda de criaturas especiales +(definidas como rojas, voladoras y no pequeñas) podría hacerse de la siguiente manera: + +```java + var specialCreaturesSelector = + new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING)).and(new SizeSelector(Size.SMALL).not()); + + var specialCreatures = creatures.stream().filter(specialCreaturesSelector) + .collect(Collectors.toList()); +``` + +**Más información sobre las especificaciones de los compuestos** + +En Composite Specification, crearemos instancias personalizadas de `AbstractSelector` combinando +otros selectores (llamados "hojas") utilizando los tres operadores lógicos básicos. Éstos se implementan en +ConjunctionSelector`, `DisjunctionSelector` y `NegationSelector`. + +```java +public abstract class AbstractSelector implements Predicate { + + public AbstractSelector and(AbstractSelector other) { + return new ConjunctionSelector<>(this, other); + } + + public AbstractSelector or(AbstractSelector other) { + return new DisjunctionSelector<>(this, other); + } + + public AbstractSelector not() { + return new NegationSelector<>(this); + } +} +``` + +```java +public class ConjunctionSelector extends AbstractSelector { + + private final List> leafComponents; + + @SafeVarargs + ConjunctionSelector(AbstractSelector... selectors) { + this.leafComponents = List.of(selectors); + } + + /** + * Tests if *all* selectors pass the test. + */ + @Override + public boolean test(T t) { + return leafComponents.stream().allMatch(comp -> (comp.test(t))); + } +} +``` + +Todo lo que queda por hacer ahora es crear selectores de hoja (ya sean de código duro o parametrizados) que +sean lo más genéricos posible, y podremos instanciar la clase ``AbstractSelector`` combinando +cualquier cantidad de selectores, como se ejemplificó anteriormente. Debemos tener cuidado, sin embargo, ya que es fácil +cometer un error al combinar muchos operadores lógicos; en particular, debemos prestar atención a la prioridad de las operaciones. +la prioridad de las operaciones. En general, la especificación compuesta es una gran manera de escribir más +código reutilizable, ya que no es necesario crear una clase Selector para cada operación de filtrado. En su lugar +simplemente creamos una instancia de ``AbstractSelector`` "sobre la marcha", utilizando selectores genéricos de "hoja" y algunos selectores booleanos básicos. +y alguna lógica booleana básica. + +**Comparación de los distintos enfoques** + +| Patrón | Uso | Pros | Contras | +|---|---|---|---| +| Especificación Codificada | Los criterios de selección son pocos y se conocen de antemano | + Fácil de implementar | - Inflexible | +| | | + Expresivo | +| Especificación Parametrizada | Los criterios de selección abarcan un amplio rango de valores (por ejemplo, masa, velocidad,...) | + Alguna flexibilidad | - Aún requiere clases de propósito especial | +| Especificación Compuesta | Hay muchos criterios de selección que se pueden combinar de múltiples maneras, por lo tanto, no es factible crear una clase para cada selector | + Muy flexible, sin requerir muchas clases especializadas | - Algo más difícil de comprender | +| | | + Admite operaciones lógicas | - Todavía necesitas crear las clases base utilizadas como hojas | +## Class diagram + +![alt text](./etc/specification.png "Specification") + +## Diagrama de clases + +Utilice el patrón Specification cuando + +* Necesitas seleccionar un subconjunto de objetos basándote en algún criterio, y refrescar la selección en varios momentos. +* Necesita comprobar que sólo se utilizan objetos adecuados para una determinada función (validación). + +## Patrones relacionados + +* Repository + +## Créditos + +* [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) diff --git a/localization/es/specification/etc/specification.png b/localization/es/specification/etc/specification.png new file mode 100644 index 000000000..60fb0402d Binary files /dev/null and b/localization/es/specification/etc/specification.png differ diff --git a/localization/es/state/README.md b/localization/es/state/README.md new file mode 100644 index 000000000..d84a0fe2e --- /dev/null +++ b/localization/es/state/README.md @@ -0,0 +1,167 @@ +--- +title: State +category: Behavioral +language: es +tag: + - Gang of Four +--- + +## También conocido como + +Objects for States + +## Propósito + +Permite que un objeto altere su comportamiento cuando cambia su estado interno. El objeto parecerá +cambiar de clase. + +## Explicación + +Ejemplo del mundo real + +> Al observar un mamut en su hábitat natural parece cambiar su comportamiento en función de la +> situación. Al principio puede parecer tranquilo, pero con el tiempo, cuando detecta una amenaza, se enfada y se vuelve peligroso para su entorno. +> peligroso para su entorno. + +En palabras sencillas + +> El patrón de estado permite a un objeto cambiar su comportamiento. + +Wikipedia dice + +> El patrón de estado es un patrón de diseño de software de comportamiento que permite a un objeto alterar su +> comportamiento cuando cambia su estado interno. Este patrón es cercano al concepto de máquinas de estado finito. +> máquinas de estado finito. El patrón de estado puede ser interpretado como un patrón de estrategia, que es capaz de cambiar una estrategia a través de invocaciones de métodos definidos por el usuario. +> estrategia a través de invocaciones de métodos definidos en la interfaz del patrón. + +**Ejemplo programático** + +Esta es la interfaz de estado y sus implementaciones concretas. + +```java +public interface State { + + void onEnterState(); + + void observe(); +} + +@Slf4j +public class PeacefulState implements State { + + private final Mammoth mammoth; + + public PeacefulState(Mammoth mammoth) { + this.mammoth = mammoth; + } + + @Override + public void observe() { + LOGGER.info("{} is calm and peaceful.", mammoth); + } + + @Override + public void onEnterState() { + LOGGER.info("{} calms down.", mammoth); + } +} + +@Slf4j +public class AngryState implements State { + + private final Mammoth mammoth; + + public AngryState(Mammoth mammoth) { + this.mammoth = mammoth; + } + + @Override + public void observe() { + LOGGER.info("{} is furious!", mammoth); + } + + @Override + public void onEnterState() { + LOGGER.info("{} gets angry!", mammoth); + } +} +``` + +Y aquí está el mammoth que contiene el Estado. + +```java +public class Mammoth { + + private State state; + + public Mammoth() { + state = new PeacefulState(this); + } + + public void timePasses() { + if (state.getClass().equals(PeacefulState.class)) { + changeStateTo(new AngryState(this)); + } else { + changeStateTo(new PeacefulState(this)); + } + } + + private void changeStateTo(State newState) { + this.state = newState; + this.state.onEnterState(); + } + + @Override + public String toString() { + return "The mammoth"; + } + + public void observe() { + this.state.observe(); + } +} +``` + +He aquí el ejemplo completo de cómo se comporta el mammoth a lo largo del tiempo. + +```java + var mammoth = new Mammoth(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); +``` + +Salida del programa: + +```java + The mammoth gets angry! + The mammoth is furious! + The mammoth calms down. + The mammoth is calm and peaceful. +``` + +## Diagrama de clases + +![alt text](./etc/state_urm.png "State") + +## Aplicabilidad + +Utiliza el patrón State en cualquiera de los siguientes casos: + +* El comportamiento de un objeto depende de su estado, y debe cambiar su comportamiento en tiempo de ejecución dependiendo de ese estado. +* Las operaciones tienen grandes sentencias condicionales multiparte que dependen del estado del objeto. Este estado suele estar representado por una o más constantes enumeradas. A menudo, varias operaciones contendrán esta misma estructura condicional. El patrón State coloca cada rama de la condicional en una clase separada. Esto permite tratar el estado del objeto como un objeto en sí mismo que puede variar independientemente de otros objetos. + +Traducción realizada con la versión gratuita del traductor www.DeepL.com/Translator + +## Usos conocidos + +* [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. +* [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) + +## 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) diff --git a/localization/es/state/etc/state_urm.png b/localization/es/state/etc/state_urm.png new file mode 100644 index 000000000..c2cf9f562 Binary files /dev/null and b/localization/es/state/etc/state_urm.png differ diff --git a/localization/es/strategy/README.md b/localization/es/strategy/README.md new file mode 100644 index 000000000..c27037aff --- /dev/null +++ b/localization/es/strategy/README.md @@ -0,0 +1,187 @@ +--- +title: Strategy +category: Behavioral +language: es +tag: + - Gang of Four +--- + +## También conocido como + +Policy + +## Propósito + +Definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. La estrategia permite +que el algoritmo varíe independientemente de los clientes que lo utilicen. + +## Explicación + +Ejemplo del mundo real + +> Matar dragones es un trabajo peligroso. Con la experiencia, se hace más fácil. Los veteranos de +> Los matadragones han desarrollado diferentes estrategias de lucha contra distintos tipos de dragones. + +En palabras sencillas + +> El patrón de estrategia permite elegir el algoritmo más adecuado en tiempo de ejecución. + +Wikipedia dice + +> En programación informática, el patrón de estrategia (también conocido como patrón de política) es un patrón de > diseño de software de comportamiento que permite seleccionar un algoritmo en tiempo de ejecución. +> patrón de diseño de software que permite seleccionar un algoritmo en tiempo de ejecución. + +**Ejemplo programático** + +Presentemos primero la interfaz de la estrategia para matar dragones y sus implementaciones. + +```java +@FunctionalInterface +public interface DragonSlayingStrategy { + + void execute(); +} + +@Slf4j +public class MeleeStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("With your Excalibur you sever the dragon's head!"); + } +} + +@Slf4j +public class ProjectileStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); + } +} + +@Slf4j +public class SpellStrategy implements DragonSlayingStrategy { + + @Override + public void execute() { + LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); + } +} +``` + +Y aquí está el poderoso cazador de dragones, que puede elegir su estrategia de lucha en función del +oponente. + +```java +public class DragonSlayer { + + private DragonSlayingStrategy strategy; + + public DragonSlayer(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } + + public void changeStrategy(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } + + public void goToBattle() { + strategy.execute(); + } +} +``` + +Por último, aquí está el cazador de dragones en acción. + +```java + LOGGER.info("Green dragon spotted ahead!"); + var dragonSlayer = new DragonSlayer(new MeleeStrategy()); + dragonSlayer.goToBattle(); + LOGGER.info("Red dragon emerges."); + dragonSlayer.changeStrategy(new ProjectileStrategy()); + dragonSlayer.goToBattle(); + LOGGER.info("Black dragon lands before you."); + dragonSlayer.changeStrategy(new SpellStrategy()); + dragonSlayer.goToBattle(); +``` + +Salida del programa: + +``` + Green dragon spotted ahead! + With your Excalibur you sever the dragon's head! + Red dragon emerges. + You shoot the dragon with the magical crossbow and it falls dead on the ground! + Black dragon lands before you. + You cast the spell of disintegration and the dragon vaporizes in a pile of dust! +``` + +Además, las expresiones lambda de Java 8 proporcionan otro enfoque para la implementación: + +```java +public class LambdaStrategy { + + private static final Logger LOGGER = LoggerFactory.getLogger(LambdaStrategy.class); + + public enum Strategy implements DragonSlayingStrategy { + MeleeStrategy(() -> LOGGER.info( + "With your Excalibur you severe the dragon's head!")), + ProjectileStrategy(() -> LOGGER.info( + "You shoot the dragon with the magical crossbow and it falls dead on the ground!")), + SpellStrategy(() -> LOGGER.info( + "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!")); + + private final DragonSlayingStrategy dragonSlayingStrategy; + + Strategy(DragonSlayingStrategy dragonSlayingStrategy) { + this.dragonSlayingStrategy = dragonSlayingStrategy; + } + + @Override + public void execute() { + dragonSlayingStrategy.execute(); + } + } +} +``` + +Y aquí está el cazador de dragones en acción. + +```java + LOGGER.info("Green dragon spotted ahead!"); + dragonSlayer.changeStrategy(LambdaStrategy.Strategy.MeleeStrategy); + dragonSlayer.goToBattle(); + LOGGER.info("Red dragon emerges."); + dragonSlayer.changeStrategy(LambdaStrategy.Strategy.ProjectileStrategy); + dragonSlayer.goToBattle(); + LOGGER.info("Black dragon lands before you."); + dragonSlayer.changeStrategy(LambdaStrategy.Strategy.SpellStrategy); + dragonSlayer.goToBattle(); +``` + +La salida del programa es la misma que la anterior. + +## Diagrama de clases + +![alt text](./etc/strategy_urm.png "Strategy") + +## Aplicabilidad + +Utilice el patrón Strategy cuando + +* Muchas clases relacionadas sólo difieren en su comportamiento. Las estrategias proporcionan una forma de configurar una clase con uno de varios comportamientos +* Necesita diferentes variantes de un algoritmo. Por ejemplo, puede definir algoritmos que reflejen diferentes compensaciones espacio/tiempo. Las estrategias pueden utilizarse cuando estas variantes se implementan como una jerarquía de clases de algoritmos +* Un algoritmo utiliza datos que los clientes no deberían conocer. Utilice el patrón Strategy para evitar exponer estructuras de datos complejas específicas del algoritmo. +* Una clase define muchos comportamientos, y éstos aparecen como múltiples sentencias condicionales en sus operaciones. En lugar de muchas condicionales, mueva las ramas condicionales relacionadas a su propia clase Strategy. + +## Tutorial + +* [Strategy Pattern Tutorial](https://www.journaldev.com/1754/strategy-design-pattern-in-java-example-tutorial) + +## 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) +* [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) diff --git a/localization/es/strategy/etc/strategy_urm.png b/localization/es/strategy/etc/strategy_urm.png new file mode 100644 index 000000000..67d19acae Binary files /dev/null and b/localization/es/strategy/etc/strategy_urm.png differ diff --git a/localization/es/subclass-sandbox/README.md b/localization/es/subclass-sandbox/README.md new file mode 100644 index 000000000..efc97b9e6 --- /dev/null +++ b/localization/es/subclass-sandbox/README.md @@ -0,0 +1,112 @@ +--- +title: Subclass Sandbox +category: Behavioral +language: es +tag: + - Game programming +--- + +## Propósito +El patrón Subclass Sandbox describe una idea básica, aunque no tiene una mecánica muy detallada. Necesitarás el patrón cuando tengas varias subclases similares. Si tienes que hacer un pequeño cambio, entonces cambia la clase base, mientras que todas las subclases no deberían tener que ser tocadas. Así que la clase base tiene que ser capaz de proporcionar todas las operaciones que una clase derivada necesita realizar. + +## Explicación +Ejemplo del mundo real +> Consideremos que queremos crear algunos superpoderes en el juego, y necesitan moverse acompañados de un efecto de sonido y desovar partículas. ¿Crear muchas clases que contengan métodos similares o necesitar una clase base para derivarlos? El patrón subclase-base te permite tratar este problema de la segunda manera. + +En palabras sencillas +> El subclass-sandbox consiste en trasladar los métodos solapados en las subclases a una clase base que reduzca la tasa de redundancia en las clases. + +Wikipedia dice +> A base class defines an abstract sandbox method and several provided operations. Marking them protected makes it clear that they are for use by derived classes. Each derived sandboxed subclass implements the sandbox method using the provided operations. + +**Ejemplo programático** +Comenzamos con la clase base `Superpower`. Contiene un método abstracto sandbox `active()` y algunas operaciones proporcionadas. + +``` +public abstract class Superpower { + + protected Logger logger; + + protected abstract void activate(); + + protected void move(double x, double y, double z) { + logger.info("Move to ( " + x + ", " + y + ", " + z + " )"); + } + + protected void playSound(String soundName, int volume) { + logger.info("Play " + soundName + " with volume " + volume); + } + + protected void spawnParticles(String particleType, int count) { + logger.info("Spawn " + count + " particle with type " + particleType); + } +} +``` +A continuación podemos crear una subclase derivada sandboxed que implemente el método sandbox usando las operaciones proporcionadas. Aquí está la primera Superpower: +``` +public class SkyLaunch extends Superpower { + + public SkyLaunch() { + super(); + logger = LoggerFactory.getLogger(SkyLaunch.class); + } + + @Override + protected void activate() { + move(0, 0, 20); + playSound("SKYLAUNCH_SOUND", 1); + spawnParticles("SKYLAUNCH_PARTICLE", 100); + } +} +``` +Aquí está la segunda Superpower. +``` +public class GroundDive extends Superpower { + + public GroundDive() { + super(); + logger = LoggerFactory.getLogger(GroundDive.class); + } + + @Override + protected void activate() { + move(0, 0, -20); + playSound("GROUNDDIVE_SOUND", 5); + spawnParticles("GROUNDDIVE_PARTICLE", 20); + } +} +``` +Por último, aquí están los superpower en activo. +``` + LOGGER.info("Use superpower: sky launch"); + var skyLaunch = new SkyLaunch(); + skyLaunch.activate(); + LOGGER.info("Use superpower: ground dive"); + var groundDive = new GroundDive(); + groundDive.activate(); +``` +Salida del programa: +``` +// Use superpower: sky launch +// Move to ( 0.0, 0.0, 20.0 ) +// Play SKYLAUNCH_SOUND with volume 1 +// Spawn 100 particle with type SKYLAUNCH_PARTICLE +// Use superpower: ground dive +// Move to ( 0.0, 0.0, -20.0 ) +// Play GROUNDDIVE_SOUND with volume 5 +// Spawn 20 particle with type GROUNDDIVE_PARTICLE +``` +## Diagrama de clases +![alt text](./etc/subclass-sandbox.urm.png "Subclass Sandbox pattern class diagram") + +## Aplicabilidad +El patrón Subclass Sandbox es un patrón muy simple y común que se encuentra en muchas bases de código, incluso fuera de los juegos. Si tienes un método protegido no virtual por ahí, probablemente ya estés usando algo como esto. Subclass Sandbox es un buen ajuste cuando: + +- Usted tiene una clase base con un número de clases derivadas. +- La clase base es capaz de proporcionar todas las operaciones que una clase derivada puede necesitar realizar. +- Hay solapamiento de comportamiento en las subclases y desea que sea más fácil compartir código entre ellas. +- Usted quiere minimizar el acoplamiento entre las clases derivadas y el resto del programa + +## Créditos + +* [Game Programming Patterns - Subclass Sandbox](https://gameprogrammingpatterns.com/subclass-sandbox.html) diff --git a/localization/es/subclass-sandbox/etc/subclass-sandbox.urm.png b/localization/es/subclass-sandbox/etc/subclass-sandbox.urm.png new file mode 100644 index 000000000..db81e12cc Binary files /dev/null and b/localization/es/subclass-sandbox/etc/subclass-sandbox.urm.png differ