diff --git a/localization/es/abstract-document/README.md b/localization/es/abstract-document/README.md new file mode 100644 index 000000000..f9b698b93 --- /dev/null +++ b/localization/es/abstract-document/README.md @@ -0,0 +1,183 @@ +--- +layout: pattern +title: Abstract Document +folder: abstract-document +permalink: /patterns/abstract-document/ +categories: Structural +language: es +tags: + - Extensibility +--- + +## Propósito + +Usar propiedades dinámicas y conseguir la felexibilidad de los lenguajes no tipados manteniendo la seguridad de tipos. + +## Explicación + +El uso del patrón Abstract Document permite gestionar propiedades no estáticas adicionales. Este patrón usa el concepto de atributos para permitir seguridad de tipos y propiedades separadas de diferentes clases en un grupo de interfaces. + +Ejemplo del mundo real + +> Toma como ejemplo un coche que está formado por muchas partes. Sin embargo, no sabemos si el coche tiene todas las partes o solo una parte de ellas. Nuestros coches son dinámicos y extremadamente flexibles. + +En resumen + +> El patrón Abstract Document permite añadir propiedades a objetos sin que estos sean conscientes de ello. + +Según Wikipedia + +> Un patrón de diseño estructural orientado a objetos para organizar objetos en contenedores clave-valor vagamente tipados y exponiendo los datos usando vistas tipadas. El propósito del patrón es conseguir un alto grado de flexibilidad entre componentes en un lenguaje de tipado fuerte donde nuevas propiedades pueden añadirse al arbol de objetos sobre la marcha sin perder el soporte de la seguridad de tipos. El patrón hace uso de atributos para separar diferentes propiedades de una clase en distintas interfaces. + +**Ejemplo Programático** + +Primero definamos las clases base `Document` y `AbstractDocument`. Básicamente hacen que el objeto contenga un mapa de propiedades y cualquier número de objetos hijo. + +```java +public interface Document { + + Void put(String key, Object value); + + Object get(String key); + + Stream children(String key, Function, T> constructor); +} + +public abstract class AbstractDocument implements Document { + + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + return Stream.ofNullable(get(key)) + .filter(Objects::nonNull) + .map(el -> (List>) el) + .findAny() + .stream() + .flatMap(Collection::stream) + .map(constructor); + } + ... +} +``` +A continuación definimos un enum `Property` y un grupo de interfaces para tipo, precio, modelo y partes. Esto nos permite crear interfaces de apariencia estática para nuestra clase `Car`. + +```java +public enum Property { + + PARTS, TYPE, PRICE, MODEL +} + +public interface HasType extends Document { + + default Optional getType() { + return Optional.ofNullable((String) get(Property.TYPE.toString())); + } +} + +public interface HasPrice extends Document { + + default Optional getPrice() { + return Optional.ofNullable((Number) get(Property.PRICE.toString())); + } +} +public interface HasModel extends Document { + + default Optional getModel() { + return Optional.ofNullable((String) get(Property.MODEL.toString())); + } +} + +public interface HasParts extends Document { + + default Stream getParts() { + return children(Property.PARTS.toString(), Part::new); + } +} +``` + +Ahora estamos listos para introducir el Coche `Car`. + +```java +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + public Car(Map properties) { + super(properties); + } +} +``` + +Y finalmente asi es como construimos y usamos el Coche `Car` en un ejemplo completo. + +```java + LOGGER.info("Constructing parts and car"); + + var wheelProperties = Map.of( + Property.TYPE.toString(), "wheel", + Property.MODEL.toString(), "15C", + Property.PRICE.toString(), 100L); + + var doorProperties = Map.of( + Property.TYPE.toString(), "door", + Property.MODEL.toString(), "Lambo", + Property.PRICE.toString(), 300L); + + var carProperties = Map.of( + Property.MODEL.toString(), "300SL", + Property.PRICE.toString(), 10000L, + Property.PARTS.toString(), List.of(wheelProperties, doorProperties)); + + var car = new Car(carProperties); + + LOGGER.info("Here is our car:"); + LOGGER.info("-> model: {}", car.getModel().orElseThrow()); + LOGGER.info("-> price: {}", car.getPrice().orElseThrow()); + LOGGER.info("-> parts: "); + car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", + p.getType().orElse(null), + p.getModel().orElse(null), + p.getPrice().orElse(null)) + ); + + // Constructing parts and car + // Here is our car: + // model: 300SL + // price: 10000 + // parts: + // wheel/15C/100 + // door/Lambo/300 +``` + +## Diagrama de clases + +![alt text](./etc/abstract-document.png "Abstract Document Traits and Domain") + +## Aplicación + +Usar el patrón Abstract Document cuando + +* Existe la necesidad de añadir propiedades sobre la marcha. +* Quieres una manera flexible de organizar el dominio en una estructura similar a un arbol. +* Quieres un sistema menos acoplado. + +## Créditos + +* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) +* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://www.amazon.com/gp/product/0470059028/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0470059028&linkId=e3aacaea7017258acf184f9f3283b492)