1.概要
抽象文档模式可以处理其他非静态属性,放入Map<String,Object> 来进行存储。 此模式使用接口中的默认方法、Optional和Stream来启用类型安全性,并将不同类的属性分离为一组接口(如下的HasModel、HasParts等等)。
2. 代码结构
-
Document接口
-
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); }
-
AbstractDocument抽象类,Document的抽象实现类
public abstract class AbstractDocument implements Document { private final Map<String, Object> properties; protected AbstractDocument(Map<String, Object> properties) { 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) { 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(); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(getClass().getName()).append("["); properties.entrySet() .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]")); builder.append("]"); return builder.toString(); } }
-
HasModel接口,继承了Document接口
public interface HasModel extends Document { String PROPERTY = "model"; default Optional<String> getModel() { return Optional.ofNullable((String) get(PROPERTY)); } }
-
HasParts接口,继承了Document接口
public interface HasParts extends Document { String PROPERTY = "parts"; default Stream<Part> getParts() { return children(PROPERTY, Part::new); } }
-
HasPrice接口,继承了Document接口
public interface HasPrice extends Document { String PROPERTY = "price"; default Optional<Number> getPrice() { return Optional.ofNullable((Number) get(PROPERTY)); } }
-
HasType接口,继承了Document接口
public interface HasType extends Document { String PROPERTY = "type"; default Optional<String> getType() { return Optional.ofNullable((String) get(PROPERTY)); } }
-
Part类,继承AbstractDocument,实现了HasType、 HasModel、 HasPrice
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { public Part(Map<String, Object> properties) { super(properties); } }
-
Car实体类,继承AbstractDocument,实现了HasType、 HasModel、 HasPrice
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { public Car(Map<String, Object> properties) { super(properties); } }
-
测试类
public static void main(String[] args) { LOGGER.info("Constructing parts and car"); // 车属性map Map<String, Object> carProperties = new HashMap<>(); carProperties.put(HasModel.PROPERTY, "300SL"); carProperties.put(HasPrice.PROPERTY, 10000L); // 轮子属性map Map<String, Object> wheelProperties = new HashMap<>(); wheelProperties.put(HasType.PROPERTY, "wheel"); wheelProperties.put(HasModel.PROPERTY, "15C"); wheelProperties.put(HasPrice.PROPERTY, 100L); // 门属性map Map<String, Object> doorProperties = new HashMap<>(); doorProperties.put(HasType.PROPERTY, "door"); doorProperties.put(HasModel.PROPERTY, "Lambo"); doorProperties.put(HasPrice.PROPERTY, 300L); carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties)); Car car = new Car(carProperties); LOGGER.info("Here is our car:"); LOGGER.info("-> model: {}", car.getModel().get()); LOGGER.info("-> price: {}", car.getPrice().get()); LOGGER.info("-> parts: "); car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", p.getType().get(), p.getModel().get(), p.getPrice().get())); }
3.使用场景
- 当你想灵活像树结构组织对象时,或者需要动态地添加属性时,采用抽象文档模式可以使系统更加松耦合
4.总结
- 所有的属性都通过Map<String,Object>存储,不需要关心具体的类型是什么。
- 对象可以有子对象。比如,Car有Wheel,door,其中wheel和door都是子对象。通过car可以获得wheel和door子对象,通过子对象设置和获取子对象的属性。
- 通过基类封装基本操作。这样不同Car之间可以共享实现。
- 该模式在强类型语言中实现了组件之间的高度灵活性,使新的属性可以在对象树上快速添加,而不会失去类型安全的支持。