读者容错模式(Tolerant Reader Pattern)

读者容错模式是当从一个数据模型中读取数据的时候,无论这个数据模型如何变换,尽最大可能读取自己需要的数据,达到读取数据健壮性的要求。例如我们有一个数据模型类:RainbowFish,表示一种鱼。有一个读写数据的类:RainbowFishSerializer,来获取RainbowFish的name、age、lengthMeters、weightTons等数据。但是RainbowFish是第三方提供,可能在迭代升级的时候改变了类结构,例如增加了子类:RainbowFishV2,记录了更加详细的信息。数据结构发生了变化,我们在读取的时候就要进行适配,避免由于改变导致的crash。

代码:

/**
 * 
 * RainbowFish is the initial schema
 *
 */
public class RainbowFish implements Serializable {

  private static final long serialVersionUID = 1L;

  private String name;
  private int age;
  private int lengthMeters;
  private int weightTons;

  /**
   * Constructor
   */
  public RainbowFish(String name, int age, int lengthMeters, int weightTons) {
    this.name = name;
    this.age = age;
    this.lengthMeters = lengthMeters;
    this.weightTons = weightTons;
  }

  public String getName() {
    return name;
  }

  public int getAge() {
    return age;
  }

  public int getLengthMeters() {
    return lengthMeters;
  }

  public int getWeightTons() {
    return weightTons;
  }

}

/**
 * 
 * RainbowFishV2 is the evolved schema
 *
 */
public class RainbowFishV2 extends RainbowFish {

  private static final long serialVersionUID = 1L;

  private boolean sleeping;
  private boolean hungry;
  private boolean angry;

  public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) {
    super(name, age, lengthMeters, weightTons);
  }

  /**
   * Constructor
   */
  public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping,
      boolean hungry, boolean angry) {
    this(name, age, lengthMeters, weightTons);
    this.sleeping = sleeping;
    this.hungry = hungry;
    this.angry = angry;
  }

  public boolean getSleeping() {
    return sleeping;
  }

  public boolean getHungry() {
    return hungry;
  }

  public boolean getAngry() {
    return angry;
  }
}
/**
 * 
 * RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to
 * file. Tolerant Reader pattern is implemented here by serializing maps instead of
 * {@link RainbowFish} objects. This way the reader does not break even though new properties are
 * added to the schema.
 *
 */
public final class RainbowFishSerializer {

  private RainbowFishSerializer() {
  }

  /**
   * Write V1 RainbowFish to file
   */
  public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException {
    Map<String, String> map = new HashMap<>();
    map.put("name", rainbowFish.getName());
    map.put("age", String.format("%d", rainbowFish.getAge()));
    map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters()));
    map.put("weightTons", String.format("%d", rainbowFish.getWeightTons()));
    try (FileOutputStream fileOut = new FileOutputStream(filename);
        ObjectOutputStream objOut = new ObjectOutputStream(fileOut)) {
      objOut.writeObject(map);
    }
  }

  /**
   * Write V2 RainbowFish to file
   */
  public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException {
    Map<String, String> map = new HashMap<>();
    map.put("name", rainbowFish.getName());
    map.put("age", String.format("%d", rainbowFish.getAge()));
    map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters()));
    map.put("weightTons", String.format("%d", rainbowFish.getWeightTons()));
    map.put("angry", Boolean.toString(rainbowFish.getAngry()));
    map.put("hungry", Boolean.toString(rainbowFish.getHungry()));
    map.put("sleeping", Boolean.toString(rainbowFish.getSleeping()));
    try (FileOutputStream fileOut = new FileOutputStream(filename);
        ObjectOutputStream objOut = new ObjectOutputStream(fileOut)) {
      objOut.writeObject(map);
    }
  }

  /**
   * Read V1 RainbowFish from file
   */
  public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException {
    Map<String, String> map = null;

    try (FileInputStream fileIn = new FileInputStream(filename);
        ObjectInputStream objIn = new ObjectInputStream(fileIn)) {
      map = (Map<String, String>) objIn.readObject();
    }

    return new RainbowFish(map.get("name"), Integer.parseInt(map.get("age")), Integer.parseInt(map.get("lengthMeters")),
        Integer.parseInt(map.get("weightTons")));
  }
}

/**
 * 
 * Tolerant Reader is an integration pattern that helps creating robust communication systems. The
 * idea is to be as tolerant as possible when reading data from another service. This way, when the
 * communication schema changes, the readers must not break.
 * <p>
 * In this example we use Java serialization to write representations of {@link RainbowFish} objects
 * to file. {@link RainbowFish} is the initial version which we can easily read and write using
 * {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link RainbowFishV2}
 * and we again write it to file with a method designed to do just that. However, the reader client
 * does not know about the new format and still reads with the method designed for V1 schema.
 * Fortunately the reading method has been designed with the Tolerant Reader pattern and does not
 * break even though {@link RainbowFishV2} has new fields that are serialized.
 *
 */
public class App {

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

  /**
   * Program entry point
   */
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    // Write V1
    RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12);
    LOGGER.info("fishV1 name={} age={} length={} weight={}", fishV1.getName(),
        fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons());
    RainbowFishSerializer.writeV1(fishV1, "fish1.out");
    // Read V1
    RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out");
    LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}",
        deserializedFishV1.getName(), deserializedFishV1.getAge(),
        deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons());
    // Write V2
    RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true);
    LOGGER.info(
        "fishV2 name={} age={} length={} weight={} sleeping={} hungry={} angry={}",
        fishV2.getName(), fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(),
        fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping());
    RainbowFishSerializer.writeV2(fishV2, "fish2.out");
    // Read V2 with V1 method
    RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out");
    LOGGER.info("deserializedFishV2 name={} age={} length={} weight={}",
        deserializedFishV2.getName(), deserializedFishV2.getAge(),
        deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons());
  }
}



猜你喜欢

转载自blog.csdn.net/THEONE10211024/article/details/77993533