【Java设计模式】抽象文档模式:以灵活性简化数据处理

抽象文档设计模式的意图

Java中的抽象文档设计模式是一种关键的结构设计模式,它通过为各种文档类型定义一个通用接口,提供了一种处理层次结构和树状数据结构的一致方法。它将核心文档结构与特定的数据格式分离,实现动态更新并简化维护。

抽象文档模式的详细解释及实际示例

Java中的抽象文档设计模式允许动态处理非静态属性。此模式使用特征的概念来实现类型安全,并将不同类的属性分离到一组接口中。
实际示例

考虑一个在Java中实现抽象文档设计模式的图书馆系统,其中书籍可以有不同的格式和属性:实体书、电子书和有声书。每种格式都有独特的属性,例如实体书的页数、电子书的文件大小和有声书的时长。抽象文档设计模式允许图书馆系统灵活地管理这些不同的格式。通过使用此模式,系统可以动态地存储和检索属性,而不需要为每种书籍类型设置严格的结构,使得在未来添加新格式或属性时无需对代码库进行重大更改。
通俗地说
抽象文档模式允许在对象上附加属性而无需对象知晓。
维基百科说
一种面向对象的结构设计模式,用于在松散类型的键值存储中组织对象,并使用类型化视图公开数据。该模式的目的是在强类型语言中的组件之间实现高度的灵活性,其中可以动态地向对象树添加新属性,同时不会失去类型安全的支持。该模式利用特征将类的不同属性分离到不同的接口中。

Java中抽象文档模式的编程示例

考虑一辆由多个部分组成的汽车。然而,我们不知道特定的汽车是否真的拥有所有的部件,或者只有其中一些。我们的汽车是动态的且极其灵活。
首先定义基类DocumentAbstractDocument。它们基本上使对象持有一个属性映射和任意数量的子对象。

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);
    }
    
    // 其他属性和方法...
}

接下来定义一个枚举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中何时使用抽象文档模式

抽象文档设计模式在需要管理具有一些共同属性或行为但也具有特定于其各自类型的独特属性或行为的不同文档类型的场景中特别有用。以下是一些可以应用抽象文档设计模式的场景:

  • 内容管理系统(CMS):在 CMS 中,可能有各种类型的内容,如文章、图像、视频等。每种类型的内容可能具有共享属性,如创建日期、作者和标签,同时也具有特定于图像的图像尺寸或视频的视频时长等特定属性。
  • 文件系统:如果正在设计一个需要管理不同类型文件的文件系统,如文档、图像、音频文件和目录,抽象文档模式可以帮助提供一种一致的方式来访问属性,如文件大小、创建日期等,同时允许特定属性,如图像分辨率或音频时长。
  • 电子商务系统:电子商务平台可能有不同的产品类型,如实体产品、数字下载和订阅。每种类型可以共享通用属性,如名称、价格和描述,同时具有实体产品的运输重量或数字产品的下载链接等独特属性。
  • 医疗记录系统:在医疗保健中,患者记录可能包括各种类型的数据,如人口统计信息、医疗历史、测试结果和处方。抽象文档模式可以帮助管理共享属性,如患者 ID 和出生日期,同时适应特定属性,如测试结果或处方药物。
  • 配置管理:在处理软件应用程序的配置设置时,可能有不同类型的配置元素,每个元素都有自己的一组属性。抽象文档模式可用于管理这些配置元素,同时确保有一种一致的方式来访问和操作它们的属性。
  • 教育平台:教育系统可能有各种类型的学习材料,如基于文本的内容、视频、测验和作业。可以共享诸如标题、作者和发布日期等常见属性,而独特属性,如视频时长或作业截止日期,可以特定于每种类型。
  • 项目管理工具:在项目管理应用程序中,可以有不同类型的任务,如待办事项、里程碑和问题。抽象文档模式可用于处理一般属性,如任务名称和负责人,同时允许特定属性,如里程碑日期或问题优先级。
  • 文档具有多样化和不断演变的属性结构。
  • 动态添加新属性是常见需求。
  • 从特定格式解耦数据访问至关重要。
  • 代码的可维护性和灵活性至关重要。
    抽象文档设计模式背后的关键思想是提供一种灵活且可扩展的方式来管理具有共享和不同属性的不同类型的文档或实体。通过定义一个通用接口并在各种文档类型中实现它,可以实现一种更有条理和一致的方法来处理复杂的数据结构。

抽象文档模式的优点和权衡

优点:

  • 灵活性:适应各种文档结构和属性。
  • 可扩展性:动态添加新属性而不破坏现有代码。
  • 可维护性:由于关注点分离,促进了干净且适应性强的代码。
  • 可重用性:类型化视图实现了对特定属性类型的代码重用。
    权衡:
  • 复杂性:需要定义接口和视图,增加了实现的开销。
  • 性能:与直接数据访问相比,可能会引入轻微的性能开销。

源码下载

https://download.csdn.net/download/weixin_42545951/89677160

参考和致谢

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值