Java 中的抽象文档模式:以灵活的方式简化数据处理
抽象文档设计模式的意图
Java 中的抽象文档设计模式是一种重要的结构型设计模式,它通过为各种文档类型定义一个通用接口,提供了一种一致的方式来处理分层和树状数据结构。它将核心文档结构与特定数据格式分离,从而实现动态更新和简化维护。
抽象文档模式的详细解释以及真实世界示例
Java 中的抽象文档设计模式允许动态处理非静态属性。这种模式使用特征的概念来实现类型安全,并将不同类的属性分离到一组接口中。
现实世界中的例子
考虑一个使用 Java 中的抽象文档设计模式实现的图书馆系统,其中书籍可以具有多种格式和属性:实体书、电子书和有声书。每种格式都有独特的属性,例如实体书的页数、电子书的文件大小以及有声书的时长。抽象文档设计模式允许图书馆系统灵活地管理这些不同的格式。通过使用这种模式,系统可以动态地存储和检索属性,而无需为每种书籍类型提供严格的结构,从而更容易在将来添加新的格式或属性,而无需对代码库进行重大更改。
通俗地说
抽象文档模式允许将属性附加到对象,而无需让对象知道这些属性。
维基百科说
面向对象的结构型设计模式,用于在松散类型键值存储中组织对象,并使用类型化视图公开数据。这种模式的目的是在强类型语言中实现组件之间的高度灵活性,在这些语言中,可以在运行时将新属性添加到对象树中,而不会失去类型安全的支持。该模式利用特征将类的不同属性分离到不同的接口中。
Java 中抽象文档模式的编程示例
考虑一辆由多个部件组成的汽车。但是,我们不知道特定汽车是否真的具有所有部件,或者只是其中一些部件。我们的汽车是动态的,非常灵活。
让我们首先定义基类 Document
和 AbstractDocument
。它们基本上使对象可以保存一个属性映射和任意数量的子对象。
public interface Document {
Void put(String key, Object value);
Object get(String key);
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
}
public abstract class AbstractDocument implements Document {
private final Map<String, Object> properties;
protected AbstractDocument(Map<String, Object> 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 <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
return Stream.ofNullable(get(key))
.filter(Objects::nonNull)
.map(el -> (List<Map<String, Object>>) el)
.findAny()
.stream()
.flatMap(Collection::stream)
.map(constructor);
}
// Other properties and methods...
}
接下来,我们定义一个枚举 Property
和一组类型、价格、型号和部件的接口。这使我们能够为 Car
类创建静态外观接口。
public enum Property {
PARTS, TYPE, PRICE, MODEL
}
public interface HasType extends Document {
default Optional<String> getType() {
return Optional.ofNullable((String) get(Property.TYPE.toString()));
}
}
public interface HasPrice extends Document {
default Optional<Number> getPrice() {
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
}
}
public interface HasModel extends Document {
default Optional<String> getModel() {
return Optional.ofNullable((String) get(Property.MODEL.toString()));
}
}
public interface HasParts extends Document {
default Stream<Part> getParts() {
return children(Property.PARTS.toString(), Part::new);
}
}
现在,我们准备介绍 Car
类。
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
public Car(Map<String, Object> properties) {
super(properties);
}
}
最后,以下是在完整示例中构建和使用 Car
类的方法。
public static void main(String[] args) {
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))
);
}
程序输出
07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Constructing parts and car
07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Here is our car:
07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> model: 300SL
07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> price: 10000
07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> parts:
07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- wheel/15C/100
07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- door/Lambo/300
抽象文档模式的类图

何时在 Java 中使用抽象文档模式
抽象文档设计模式在需要管理 Java 中不同文档类型的情况下特别有用,这些文档类型共享一些通用属性或行为,但也有特定于其自身类型的独特属性或行为。以下是一些可以使用抽象文档设计模式的场景
内容管理系统 (CMS):在 CMS 中,您可能有多种类型的內容,例如文章、图片、视频等。每种类型的內容可能具有共享的属性,例如创建日期、作者和标签,但也具有特定的属性,例如图片尺寸或视频时长。
文件系统:如果您正在设计一个需要管理不同类型文件的系统,例如文档、图片、音频文件和目录,抽象文档模式可以帮助您提供一种一致的方式来访问诸如文件大小、创建日期等属性,同时允许特定的属性,例如图片分辨率或音频时长。
电子商务系统:电子商务平台可能具有不同的产品类型,例如实体产品、数字下载和订阅。每种类型都可能共享诸如名称、价格和描述等通用属性,但也具有独特的属性,例如实体产品的运费重量或数字产品的下载链接。
医疗记录系统:在医疗保健中,患者记录可能包含多种类型的数据,例如人口统计学、病史、测试结果和处方。抽象文档模式可以帮助管理诸如患者 ID 和出生日期等共享属性,同时适应测试结果或处方药物等专门属性。
配置管理:在处理软件应用程序的配置设置时,可能存在不同类型的配置元素,每个元素都有自己的一组属性。抽象文档模式可用于管理这些配置元素,同时确保以一致的方式访问和操作其属性。
教育平台:教育系统可能具有多种类型的学习资料,例如文本内容、视频、测验和作业。诸如标题、作者和出版日期等通用属性可以共享,而视频时长或作业截止日期等独特属性可以特定于每种类型。
项目管理工具:在项目管理应用程序中,您可能拥有不同类型的任务,例如待办事项、里程碑和问题。抽象文档模式可用于处理诸如任务名称和任务分配人等通用属性,同时允许诸如里程碑日期或问题优先级等特定属性。
文档具有多样化且不断变化的属性结构。
动态添加新属性是常见的要求。
将数据访问与特定格式解耦至关重要。
可维护性和灵活性对于代码库至关重要。
抽象文档设计模式背后的关键思想是提供一种灵活且可扩展的方式来管理具有共享和不同属性的不同类型的文档或实体。通过定义一个通用接口并在各种文档类型中实现它,您可以获得一种更组织化和一致的方法来处理复杂的数据结构。
抽象文档模式的优点和权衡
优点
灵活性:适应不同的文档结构和属性。
可扩展性:动态添加新属性,而不会破坏现有代码。
可维护性:由于关注点分离,促进了干净且适应性强的代码。
可重用性:类型化视图使代码能够重用以访问特定属性类型。
权衡
复杂性:需要定义接口和视图,增加实现开销。
性能:与直接数据访问相比,可能会引入轻微的性能开销。