Patrón de diseño estructural orientado a objetos para organizar objetos en almacenes de clave-valor de tipo flexible y exponer los datos mediante vistas tipificadas. El propósito del patrón es lograr un alto grado de flexibilidad entre los componentes en un lenguaje fuertemente tipado donde se pueden agregar nuevas propiedades al árbol de objetos sobre la marcha, sin perder el soporte de la seguridad de tipos. El patrón hace uso de rasgos para separar diferentes propiedades de una clase en diferentes interfaces. [1] El término "documento" está inspirado en las bases de datos orientadas a documentos .
Un documento es un objeto que contiene una serie de propiedades. Una propiedad puede ser, por ejemplo, un valor como un número o una cadena, o puede ser una lista de otros documentos. Cada propiedad se referencia mediante una clave. [2] Al recorrer el árbol de documentos, el usuario especifica un constructor que se utilizará para crear la clase de implementación del siguiente nivel. Las implementaciones suelen ser una unión de varios rasgos que extienden la interfaz de Document, lo que les permite gestionar la configuración y la obtención de propiedades por sí solas.
La interfaz "Documento" indica que las propiedades se pueden editar utilizando el método "put", leer utilizando el método "get" y los subdocumentos se pueden recorrer utilizando el método "children". El método "children" requiere una referencia funcional a un método que pueda producir una vista tipificada de un documento secundario dado un mapa de los datos que el documento secundario debería tener. El mapa debería ser un puntero al mapa original para que los cambios en la vista también afecten al documento original.
Las implementaciones pueden heredar de múltiples interfaces de características que describen diferentes propiedades. Varias implementaciones pueden incluso compartir el mismo mapa; la única restricción que el patrón impone al diseño de la implementación es que debe ser sin estado, excepto para las propiedades heredadas de "BaseDocument".
Interfaz Documento put(clave: String, valor: Object): Objeto obtener(clave: cadena): objeto hijos(clave: String, constructor: Map<String, Object> -> T) : T[] clase abstracta BaseDocument : Documento propiedades: Mapa<Cadena, Objeto> constructor (propiedades: Map<String, Object>) esto->propiedades := propiedades implementar put(clave: String, valor: Object) : Object return this->properties->put(clave, valor) implementar get(key : String) : Objeto retorna this->properties->get(key) implementar hijos(clave: String, constructor: Map<String, Object> -> T) : T[] var resultado := new T[] var children := this->properties->get(key) castTo Map<String, Object>[] foreach ( child in children ) resultado[] := constructor->aplicar(hijo) devolver resultado
El patrón de documento abstracto permite al desarrollador almacenar variables como ajustes de configuración en una estructura de árbol sin tipo y operar en los documentos utilizando vistas tipificadas. Se pueden crear nuevas vistas o implementaciones alternativas de vistas sin afectar la estructura interna del documento. La ventaja de esto es un sistema acoplado de forma más flexible, pero también aumenta el riesgo de errores de conversión, ya que el tipo heredado de una propiedad no siempre es seguro.
La implementación completa del patrón Documento abstracto está disponible en https://java-design-patterns.com/patterns/abstract-document/. A continuación, se detallan las clases clave.
Documento.java
interfaz pública Documento { Objeto put ( String clave , Objeto valor ); Objeto get ( String clave ); < T > Stream < T > hijos ( String clave , Función < Mapa < String , Objeto > , T > constructor ); ); }
Documento base.java
clase abstracta pública AbstractDocument implementa Document { private final Map < String , Object > propiedades ; protected AbstractDocument ( Map < String , Object > propiedades ) { Objects . requireNonNull ( propiedades , "se requiere el mapa de propiedades" ); this . properties = properties ; } @Override public Void put ( String clave , Object valor ) { properties . put ( clave , valor ); return null ; } @Override public Object get ( String clave ) { return properties . get ( clave ); } @Override public < T > Stream < T > children ( String clave , Function < Map < String , Object > , T > constructor ) { return Stream . ofNullable ( get ( clave )) . filter ( Objects :: nonNull ) . map ( el -> ( List < Map < String , Object >> ) el ) . findAny () . stream () . flatMap ( Collection :: stream ) . mapa ( constructor ); } }
Uso.java
var carProperties = ...; var car = new Car ( carProperties ); String modelo = car.getModel ( ). orElseThrow ( )); int precio = car.getPrice ( ) . orElseThrow ( ) ); var partes = car.getParts ();