Document.interface
/**
* Document 接口
*/
public interface Document {
/**
* 设置目标键相关联的值
*
* @param key element key
* @param value element value
* @return Void
*/
Void put(String key, Object value);
/**
* 得到目标键相关联的值
*
* @param key element key
* @return value or null
*/
Object get(String key);
/**
* 得到孩子文档
*
* @param key element key
* @param constructor constructor of child class
* @return child documents
*/
<T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor);
}
上面三个方法就是抽象文档的三个接口,put()和get()方法很好理解,问题关键在于理解children方法,何为得到孩子文档?
举个例子,一辆小汽车有很多属性组成,轮子 ,窗户,发动机,…..很多时候我们想知道小汽车自身的价格,以及他的轮子,窗户。后面又想知道汽车的牌子是奥迪还是宝马。如果依依定义出确切的对象以及子对象是非常痛苦的。但是我们似乎可以定义出一组共用的类型,赋给小汽车和他的组成对象:
小汽车(root document)
model(模型) | price(价值) | parts(部分) |
---|
300SL | 10000 | 轮子,窗户 |
轮子(children document)
type(类型) | model(模型) | price(价值) |
---|
wheel | 15C | 100 |
门(children document)
type(类型) | model(模型) | price(价值) |
---|
door | Lambo | 300 |
App.class
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Executes the App
*/
public App() {
LOGGER.info("Constructing parts and car");
Map<String, Object> carProperties = new HashMap<>();
carProperties.put(HasModel.PROPERTY, "300SL");
carProperties.put(HasPrice.PROPERTY, 10000L);
Map<String, Object> wheelProperties = new HashMap<>();
wheelProperties.put(HasType.PROPERTY, "wheel");
wheelProperties.put(HasModel.PROPERTY, "15C");
wheelProperties.put(HasPrice.PROPERTY, 100L);
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()
}
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
new App();
}
结果输出:
- Constructing parts and car
- Here is our car:
- -> model: 300SL
- -> price: 10000
- -> parts:
- wheel/15C/100
- door/Lambo/300
AbstractDocument.class
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) {
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.interface
public interface HasModel extends Document {
String PROPERTY = "model";
default Optional<String> getModel() {
return Optional.ofNullable((String) get(PROPERTY));
}
}
HasParts.interface
public interface HasParts extends Document {
String PROPERTY = "parts";
default Stream<Part> getParts() {
return children(PROPERTY, Part::new);
}
}
HasPrice .interface
public interface HasPrice extends Document {
String PROPERTY = "price";
default Optional<Number> getPrice() {
return Optional.ofNullable((Number) get(PROPERTY));
}
}
HasType.interface
public interface HasType extends Document {
String PROPERTY = "type";
default Optional<String> getType() {
return Optional.ofNullable((String) get(PROPERTY));
}
}
Part.class
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
public Part(Map<String, Object> properties) {
super(properties);
}