java-----AbstractDocument

本文探讨了Java中的AbstractDocument类及其与Part和Car的关系。Part和Car在结构上既是平级,又是父子关系。通过实现HasParts等接口,提供了getXXX()方法以访问和操作各个部分的属性。接口继承自Document,具备检查和遍历子属性的能力,通过Document.children获取子属性的Stream并使用forEach进行处理。在hasParts的getParts方法中,利用构造函数将Parts的子属性转化为AbstractDocument对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

document

package com.iluwatar.abstractdocument;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
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);
}

abstract-document

package com.iluwatar.abstractdocument;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
public abstract class AbstractDocument implements Document {
  //a properties map,if a property doesn't contain sub-property,
  //then it directly return a Object which stand for the property it self
  //and if a property does contain sub-property,then the Object
  //refer to it is a list of map,and each map is property's name - property's object map
  private final Map<String, Object> properties;
  protected AbstractDocument(Map<String, Object> properties) {
    Objects.requireNonNull(properties, "properties map is required");
    this.properties = properties;
  }
  //put a property into properties
  @Override
  public Void put(String key, Object value) {
    properties.put(key, value);
    return null;
  }
  //directly return the non-sub-property's object itself
  @Override
  public Object get(String key) {
    return properties.get(key);
  }
  //return a list of map of has-sub-property property,each map contain key-object pair
  //you can operate the result Stream<T> like this:
  //result.forEach((r)->{//doSomething});
  //since forEach method look for every sub-property object and invoke the lambda expression
  @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);
  }
  @Override
  public String toString() {
    var builder = new StringBuilder();
    builder.append(getClass().getName()).append("[");
    properties.forEach((key, value) -> builder.append("[").append(key).append(" : ").append(value)
        .append("]"));
    builder.append("]");
    return builder.toString();
  }
}

hasParts

public interface HasParts extends Document {


  default Stream<Part> getParts() {
    return children(Property.PARTS.toString(), Part::new);
  }

}

Parts

public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {

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

Car

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

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

}

App

/*
 * The MIT License
 * Copyright © 2014-2019 Ilkka Seppälä
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.iluwatar.abstractdocument;

import com.iluwatar.abstractdocument.domain.Car;
import com.iluwatar.abstractdocument.domain.enums.Property;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 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.
 *
 * <p>In Abstract Document pattern,({@link AbstractDocument}) fully implements {@link Document})
 * interface. Traits are then defined to enable access to properties in usual, static way.
 */
public class App {

  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

  /**
   * Executes the App.
   */
  public App() {
    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))
    );
  }

  /**
   * Program entry point.
   *
   * @param args command line args
   */
  public static void main(String[] args) {
    new App();
  }

}

对于每个部分,HasModel,HasPrice,HasParts…这些接口都实现了,getXXX()形式的函数
而且由于这些接口又都继承子Document,意味着这些接口拥有判断这个该属性是否拥有子属性的能力,
因为如果拥有子属性,那么调用getXXX的时候就直接调用Document.children来返回一包含一串Object的Stream,可以通过Stream.forEach来遍历和操作每个子属性的Object

有几个巧妙的地方值得提一下
1.Part和Car既是平级,又是父子关系
2.hasParts的getParts要传递给children一个构造函数,让Parts的子属性构造成一个AbstractDocument

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值