读者容错模式(Tolerant Reader Pattern)

来源:互联网 发布:淘宝订单能隐藏吗 编辑:程序博客网 时间:2024/06/04 20:02

读者容错模式是当从一个数据模型中读取数据的时候,无论这个数据模型如何变换,尽最大可能读取自己需要的数据,达到读取数据健壮性的要求。例如我们有一个数据模型类: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());  }}



原创粉丝点击