文档是包含许多属性的对象,而属性可以是数字或字符串之类的值,也可以是其他文档的列表。使用键Key引用每个属性。当遍历文档树时,用户指定用于创建下一级别的实现类的构造函数。这些实现通常是扩展Document接口的各种特性的联合,使它们可以自己处理设置和获取属性。
“文档Document”表示可以使用“put”方法编辑属性,使用“get”方法读取,可以使用“children”方法遍历子文档。“children”方法需要对一个方法的功能性引用,该方法可以在给定子项应具有的数据的映射的情况下生成子类型的视图。Map应该是指向原始Map的指针,以便视图中的更改也会影响原始文档。
实现可以从描述不同属性的多个特征接口继承。多个实现甚至可以共享相同的映射,模式对实现设计的唯一限制是它必须是无状态的,除了从“BaseDocument”继承的属性。
抽象文档模式允许开发人员在非类型化树结构中存储配置设置等变量,并使用类型化视图对文档进行操作。可以在不影响内部文档结构的情况下创建视图的新视图或替代实现。这样做的优点是系统更松散耦合,但它也增加了转换错误的风险,因为属性的继承类型并不总是确定的。
Document.java
public interface Document { Object put(String key, Object value); Object get(String key); <T> Stream<T> children( String key, Function<Map<String, Object>, T> constructor ); }
|
BaseDocument.java
public abstract class BaseDocument implements Document { private final Map<String, Object> entries; protected BaseDocument(Map<String, Object> entries) { this.entries = requireNonNull(entries); } @Override public final Object put(String key, Object value) { return entries.put(key, value); } @Override public final Object get(String key) { return entries.get(key); } @Override public final <T> Stream<T> children( String key, Function<Map<String, Object>, T> constructor) { final List<Map<String, Object>> children = (List<Map<String, Object>>) get(key); return children == null ? Stream.empty() : children.stream().map(constructor); } }
|
Usage.java
Map<String, Object> source = ...; Car car = new Car(source); String model = car.getModel(); int price = car.getPrice(); List<Wheel> wheels = car.children("wheel", Wheel::new) .collect(Collections.toList());
|
什么时候使用抽象文档模式?
- 需要动态添加新属性
- 你想要一种灵活的方式来组织树状结构中的域
- 你想要更松散耦合的系统