Java 设计模式 - Abstract Document

Abstract Document

1、Intent

Use dynamic properties and achieve flexibility of untyped languages while keeping type-safety.

动态属性可以使得类型不明确的编程语言更加灵活,同时保持类型安全。

在静态类型语言中,变量的类型在编译时就已经确定,因此在运行时无法改变变量的类型。这样的好处是可以保证代码的类型安全,但是在某些情况下会导致编码的不灵活。

相比之下,动态类型语言可以更灵活地处理数据类型。在动态类型语言中,变量的类型在运行时确定,因此可以在程序执行过程中根据需要更改变量的类型。这种灵活性使得编程变得更加简单和方便,但也增加了代码出错的风险。

动态属性可以使得静态类型语言在某些情况下具有与动态类型语言相似的灵活性。它允许在运行时动态地添加和删除属性,从而实现动态类型语言中类似于对象的行为。但是,这种灵活性并不会影响类型安全,因为属性的类型仍然在编译时就已经确定,所以可以避免因类型不匹配而导致的运行时错误。因此,使用动态属性可以在某些情况下提高编码的灵活性,同时保持类型安全。

2、Explanation

The Abstract Document pattern enables handling additional, non-static properties. This pattern uses concept of traits to enable type safety and separate properties of different classes into set of interfaces.

抽象文档模式(Abstract Document Pattern)可以处理额外的、非静态属性。这种模式使用特质(Trait)的概念来实现类型安全,并将不同类的属性分离为一组接口。

在编程中,通常会有许多类具有不同的属性和方法,但有时需要动态地添加或删除一些属性,这样的需求无法用静态类来实现。抽象文档模式通过使用特质来实现这种需求,将类的属性分离成一组接口。每个特质定义一个属性集合,表示类具有的属性和方法。通过将多个特质组合在一起,可以定义一个类的完整属性集合。

这种模式提供了类型安全,因为每个特质都是一个接口,属性和方法的类型已经确定。此外,它也提高了代码的可维护性和可扩展性,因为特质可以在不影响其他类的情况下进行修改和扩展。

总之,抽象文档模式使用特质将类的属性分离成一组接口,实现动态添加或删除属性的需求,同时保持类型安全,提高代码的可维护性和可扩展性。

2.1 Real world example

Consider a car that consists of multiple parts. However we don’t know if the specific car really has all the parts, or just some of them. Our cars are dynamic and extremely flexible.

2.2 In plain words

Abstract Document pattern allows attaching properties to objects without them knowing about it.

2.3 Wikipedia says

An object-oriented structural design pattern for organizing objects in loosely typed key-value stores and exposing the data using typed views. The purpose of the pattern is to achieve a high degree of flexibility between components in a strongly typed language where new properties can be added to the object-tree on the fly, without losing the support of type-safety. The pattern makes use of traits to separate different properties of a class into different interfaces.

在这里插入图片描述

3、Programmatic Example

Let’s first define the base classes Document and AbstractDocument. They basically make the object hold a property map and any amount of child objects.

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);
  }
  ...
}

Next we define an enum Property and a set of interfaces for type, price, model and parts. This allows us to create static looking interface to our Car class.

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);
  }
}

Now we are ready to introduce the Car.

public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {

  public Car(Map<String, Object> properties) {
    super(properties);
  }
}

And finally here’s how we construct and use the Car in a full example.

    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

4、Class diagram

在这里插入图片描述

5、 Applicability

Use the Abstract Document Pattern when

  • There is a need to add new properties on the fly
  • You want a flexible way to organize domain in tree like structure
  • You want more loosely coupled system

使用抽象文档模式(Abstract Document Pattern)的情况包括:

  1. 需要动态添加属性:当需要在运行时动态添加属性时,使用抽象文档模式可以方便地添加新属性,而无需修改代码。
  2. 需要一种树状结构来组织领域:抽象文档模式可以用于创建一种树状结构,以便组织和管理领域对象。例如,一个订单可以包含多个产品,每个产品可以有自己的属性,这些属性可以按照树形结构组织起来。
  3. 需要更松散的耦合系统:通过使用抽象文档模式,类的属性可以分离成一组接口,使得系统更加松散地耦合在一起。这意味着当需要修改或扩展系统时,只需要修改或添加相应的特质,而无需修改其他部分的代码。

总之,抽象文档模式可以在需要动态添加属性、需要组织领域对象成为树状结构或需要更松散耦合的系统时使用。这种模式可以提供灵活性、可维护性和可扩展性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式Abstract Factory Pattern) - 单例模式(Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值