设计模式之抽象文档模式

抽象文档模式的意图是保持类型安全的情况下实现无类型语言的灵活性, 通过抽象的方式更加灵活的配置类的属性,更加安全对属性进行配置


来自维基百科的介绍——抽象文档模式

面向对象的结构设计模式,用于在松散类型的键值存储中组织对象并使用类型化视图公开数据。该模式的目的是在强类型语言中实现组件之间的高度灵活性,其中可以动态地将新属性添加到对象树,而不会失去对类型安全的支持。该模式利用特征将类的不同属性分成不同的接口。“文档”的灵感来自面向文档的数据库。

设计模式之抽象文档模式

抽象文档模式的适用性和特点

  • 需要动态添加新属性时而不影响组织结构,类属性变化频率较大
  • 想要一种灵活的方式来组织树状结构中的域
  • 想要更松散耦合的系统
  • 通过集合存储属性
  • 建立属性表统一维护类的属性
  • 通过接口来配置获取和添加属性的方式

实例

1.抽象出基类,提供存储属性的集合。
2.通过接口定义存储和获取的方法

设计模式之抽象文档模式
代码示例类图
  • HasType 类型属性
  • HasPrice 价格属性
  • HasColor 颜色属性
  • HasSize 尺码属性
  • HasSeason 季节属性
  • HasClothes 用于关联下级映射关系
  • Goods 商品父接口
  • Hat 帽子实体
  • Clothes 衣服实体
  • Costume 服饰实体,实现HasClothes即可设置Clothes相关属性
/**
 * 衣服类别
 */
public interface HasClothes extends Goods {
    String PROPERTY = "clothes";
    default Stream<Clothes> getClothes() {
        return children(PROPERTY, Clothes::new);
    }
}
/**
 * 颜色属性
 */
public interface HasColor extends Goods {
    String PROPERTY = "color";
    default Optional<String> getColor() {
        return Optional.ofNullable((String) get(PROPERTY));
    }
}
/**
 * 价格属性
 */
public interface HasPrice extends Goods {
    String PROPERTY = "price";
    default Optional<String> getPrice() {
        return Optional.ofNullable((String) get(PROPERTY));
    }
}
/**
 * 季节属性
 */
public interface HasSeason extends Goods {
    String PROPERTY = "season";
    default Optional<String> getSeason() {
        return Optional.ofNullable((String) get(PROPERTY));
    }
}
/**
 * 尺码属性
 */
public interface HasSize extends Goods {
    String PROPERTY = "size";
    default Optional<String> getSize() {
        return Optional.ofNullable((String) get(PROPERTY));
    }
}
/**
 * 类型属性
 */
public interface HasType extends Goods {
    String PROPERTY = "type";
    default Optional<String> getType() {
        return Optional.ofNullable((String) get(PROPERTY));
    }
}
/**
 * 商品接口的抽象实现
 */
public abstract class AbstractGoods implements com.company.base.Goods {
    private final Map<String, Object> properties;
    protected AbstractGoods(Map<String, Object> properties) {
        // JDK工具类,是一些静态方法组成,主要用于操作对象、计算对象的哈希码,返回对象的字符串和比较两个对象
        Objects.requireNonNull(properties, "properties map is required");
        this.properties = properties;
    }
    @Override
    public Object get(String key) {
        return properties.get(key);
    }

    @Override
    public void put(String key, Object value) {
        properties.put(key, value);
    }

    @Override
    public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) {
        Optional<List<Map<String, Object>>> any = Stream.of(get(key)).filter(el -> el != null).map(el -> (List<Map<String, Object>>) el).findAny();
        return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty();
    }
}
/**
 * 商品的超级接口
 */
public interface Goods {
    void put(String key, Object value);
    Object get(String key);
    <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
}
/**
 * 衣服的实体类
 */
public class Clothes extends AbstractGoods implements HasPrice, HasColor, HasType, HasSize {
    public Clothes(Map<String, Object> properties) {
        super(properties);
    }
}
/**
 * 服饰的实体
 */
public class Costume extends AbstractGoods implements HasSeason, HasClothes{
    public Costume(Map<String, Object> properties) {
        super(properties);
    }
}
/**
 * 帽子的实体类
 */
public class Hat extends AbstractGoods implements HasPrice, HasColor, HasType, HasSize {
    public Hat(Map<String, Object> properties) {
        super(properties);
    }
}
/**
 * 一种面向对象的结构设计模式,用于在松散类型的键值存储中组织对象并使用类型化视图公开数据。
 * 该模式的目的是在强类型语言中实现组件之间的高度灵活性,其中可以动态地将新属性添加到对象
 * 树,而不会失去对类型安全的支持。该模式利用特征将类的不同属性分成不同的接口
 */
public class App {
    private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

    public App() {
        Map<String, Object> clothesProperties = new HashMap<>();
        clothesProperties.put(HasSize.PROPERTY, "XXL");
        clothesProperties.put(HasPrice.PROPERTY, "399元");
        clothesProperties.put(HasColor.PROPERTY, "棕色带图案");
        clothesProperties.put(HasType.PROPERTY, "男士上衣");

        Map<String, Object> clothes1Properties = new HashMap<>();
        clothes1Properties.put(HasSize.PROPERTY, "中号");
        clothes1Properties.put(HasPrice.PROPERTY, "188元");
        clothes1Properties.put(HasColor.PROPERTY, "黑色");
        clothes1Properties.put(HasType.PROPERTY, "鸭舌帽");

        Map<String, Object> costumeProperties = new HashMap<>();
        costumeProperties.put(HasSeason.PROPERTY, "春季新款");
        costumeProperties.put(HasClothes.PROPERTY,
                Arrays.asList(clothesProperties, clothes1Properties));

        com.company.Costume costume = new com.company.Costume(costumeProperties);

        LOGGER.debug("季节上新:");

        LOGGER.debug("-------------------------");
        LOGGER.debug("--> 季节: {}", costume.getSeason().get());

        LOGGER.debug("--> 明细:  ");
        costume.getClothes().forEach(clothes -> LOGGER.debug("-->\t {}/{}/{}/{}",
                clothes.getPrice().get(), clothes.getColor().get(),
                clothes.getSize().get(), clothes.getType().get()));

    }

    public static void main(String[] args) {
        new App();
    }
}

设计模式之抽象文档模式
执行结果

总结

  1. 所有的属性都通过Map存储。所以存储的时候不需要关心具体的类型是什么。
  2. 对象可以有子对象。比如,Costume有Hat,Clothes。Hat和Clothes都是子对象。通过Costume可以获得Hat和Clothes子对象,通过子对象设置和获取子对象的属性。
  3. 通过继承接口,实现获取类型相关的属性。Costume继承并实现接口HasSeason。如果想获得 Costume的season属性,需要调用getSeason().get()。从而实现取出的属性类型相关。
  4. 通过基类封装基本操作。这样不同Costume或者Costume和Hat、Clothes之间可以共享实现。
(5)
上一篇 2019-04-05 16:35
下一篇 2019-04-13 18:12

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注