使用抽象文档模式 为对象动态添加属性

抽象文档模式 abstract-document

  • 为什么叫抽象文档模式?

    在文件系统中,我们一个文件夹内可以有多个文件,也可以有多个文件夹,子文件夹内又可以有文件和文件夹,是树形的结构. 抽象文档模式就是对这种情况的一种抽象,可以动态的给对象添加属性来支持这种数据结构.

  • 能干什么?

    我们通常编程的过程都是先声明一个对象,对象内包含事先定义好的属性,类似于以下的这种情况↓↓↓

public class Head {
    private String head;
}

public class Arm {
    private String arm;
}

import lombok.Data;
@Data
public class Person {
    private Head head;
    private Arm leftArm;
    private Arm rightArm;
}

public static void main(String[] args) {
        Person person = new Person();
        person.setHead(new Head("头"));
        person.setLeftArm(new Arm("左胳膊"));
        person.setRightArm(new Arm("右胳膊"));
        System.out.println(person);
 }

可是有时后台的属性是动态的,能添加,能减少.这时候我们通常选择在数据库保存一个json格式的字符串, 而属性的key值基本上是写死的, 前后端对应上就ok,高级点的保存到字典表里.这种方式在实际开发中经常采用,也没什么问题,唯一缺点就是太随意,不好维护.
而抽象模式就是处理这种情况的方法之一

  • 怎么用?
    我们要把所有对象都抽象为文档. 每个文档都有自己的属性, 还能获取自己的子文档, 那么这个对象得拥有一个map来保存自己的所有属性和子文档,还需要put,get方法来获取属性,最后还应该有一个children方法,能够获取自己所有的子文档.
  1. 首先定义一个Document接口
public interface Document {

    Object get(String key);

    void set(String key, Object value);

    //获取所有子文档
    <T> Stream<T> children(String key, Function<Map<String, Object>, T> function);
}
  1. 然后定义抽象类,把接口实现
public abstract class AbstractDocument implements Document {

    private Map<String, Object> properties;

    protected AbstractDocument(Map<String, Object> properties) {
        this.properties = properties;
    }

    @Override
    public Object get(String key) {
        return properties.get(key);
    }

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

    @Override
    public <T> Stream<T> children(String key, Function<Map<String, Object>, T> function) {
        return Stream.of(properties.get(key))
                .filter(Objects::nonNull)
                .map(el -> (List<Map<String,Object>>) el)
                .findAny()
                .stream()
                .flatMap(Collection::stream)
                .map(function);
    }
}

此时我们的抽象文档已经定义好了, 接下来的对象只要继承AbstractDocument类就会拥有一个map来保存属性,set,get,children方法来操作map.

  1. 下面就是将我们实际的对象都抽象成 抽象文档 的时候了,这里以构造一个电脑为例,简单的设定电脑有CPU,Model.Price三个属性
public enum Property {
    MODEL, PRICE, CPU;
}

4.然后定义三个接口,分别表示我们拥有某一种属性 实质上就是为Docunment类提供了默认的实现, 例如: 调用HasModel对象的getModel()方法,就避免了使用document.get(“key值字符串”); 注意HasCpu接口的getCpus()方法提供了获取子类的实现方法

public interface HasModel extends Document {
    default Object getModel() {
        return get(Property.MODEL.toString());
    }
}

public interface HasPrice extends Document {
    default Object getPrice() {
        return get(Property.PRICE.toString());
    }
}

public interface HasCpu extends Document {
    default Stream<Cpu> getCpus() {
        return children(Property.CPU.toString(), Cpu::new);
    }
}

5.然后是实现Cpu和Computer两个类

public class Cpu extends AbstractDocument implements HasModel, HasPrice {
    public Cpu(Map<String, Object> properties) {
        super(properties);
    }
}

public class Computer extends AbstractDocument implements HasModel,HasPrice, HasCpu {
    protected Computer(Map<String, Object> properties) {
        super(properties);
    }
}
  1. 此时电脑已经可以组装了
    public static void main(String[] args) {
        var cpu1 = Map.of(
                Property.MODEL.toString(), "cpu1",
                Property.PRICE.toString(), "100$");

        var cpu2 = Map.of(
                Property.MODEL.toString(), "cpu2",
                Property.PRICE.toString(), "200$");

        var computerMap = Map.of(
                Property.MODEL.toString(), "联想电脑s1",
                Property.PRICE.toString(), "3000$",
                Property.CPU.toString(), List.of(cpu1, cpu2)
        );

        Computer computer = new Computer(computerMap);
        System.out.println(computer.getPrice());
        System.out.println(computer.getModel());

        computer.getCpus().forEach(el -> {
                    System.out.println(el.getModel());
                    System.out.println(el.getPrice());
                }
        );
    }
	
输出如下:
3000$
联想电脑s1
cpu1
100$
cpu2
200$

  • 如果我们再为电脑加上两个硬盘,那岂不是要先在Property类中加个DISK属性,然后再加一个HasDisk接口,在让Computer实现HasDisk接口?
    如果不嫌麻烦可以这么做,但是抽象文档模式的初衷不是要做这种事的, 他对于属性固定,拥有树形结构的对象有较好的表现 就像他的名字,抽象文档
    所以如果你有需求实现类似文件系统的对象,又希望能够动态的添加属性,那么抽象文档模式是一个很好的选择

  • 项目地址 https://gitee.com/zzkzzk/design-pattern.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值