Home Resources

A Little Java, A Few Patterns 笔记

Table of Contents

定义寿司

public class Main {
  abstract static class Shish {}

  static class Skewer extends Shish {
    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    public Onion(Shish s) { this.s = s; }

    public String toString() { return "--onion" + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Skewer(),
        new Onion(new Skewer()),
        new Tomato(new Skewer()),
        new Onion(new Onion(new Skewer())),
        new Onion(new Tomato(new Onion(new Skewer())))
      }) {
      System.out.println(s);
    }
  }
}
->
--onion->
--tomato->
--onion--onion->
--onion--tomato--onion->

寿司里是否只有洋葱?

public class Main {
  abstract static class Shish {
    abstract Boolean onlyOnions();
  }

  static class Skewer extends Shish {
    Boolean onlyOnions() { return true; }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    public Onion(Shish s) { this.s = s; }

    Boolean onlyOnions() { return s.onlyOnions(); }

    public String toString() { return "--onion" + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    Boolean onlyOnions() { return false; }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Skewer(),
        new Onion(new Onion(new Skewer())),
        new Onion(new Tomato(new Onion(new Skewer()))),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer()))))
      }) {
      System.out.println(String.format("%40s : onlyOnions=%5s", s, s.onlyOnions()));
    }
  }
}
                              -> : onlyOnions= true
                --onion--onion-> : onlyOnions= true
        --onion--tomato--onion-> : onlyOnions=false
--onion--tomato--tomato--onion-> : onlyOnions=false

这里,对空扦子也返回 onlyOnions=true, 考虑如何修正这一点。后文都认为对于空扦子 onlyOnions=true.

移除寿司里的洋葱

public class Main {
  abstract static class Shish {
    abstract Boolean onlyOnions();
    abstract Shish remOnion();
  }

  static class Skewer extends Shish {
    Boolean onlyOnions() { return true; }
    Shish remOnion() { return new Skewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    public Onion(Shish s) { this.s = s; }

    Boolean onlyOnions() { return s.onlyOnions(); }
    Shish remOnion() { return s.remOnion(); }

    public String toString() { return "--onion" + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    Boolean onlyOnions() { return false; }
    Shish remOnion() { return new Tomato(s.remOnion()); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Onion(new Onion(new Skewer())),
        new Onion(new Tomato(new Onion(new Skewer()))),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer())))),
        new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer())))))
      }) {
      System.out.println(String.format(
          "origin: %40s onlyOnions=%5s\n   new: %40s onlyOnions=%5s",
          s, s.onlyOnions(), s.remOnion(), s.remOnion().onlyOnions()));
    }
  }
}
origin:                         --onion--onion-> onlyOnions= true
   new:                                       -> onlyOnions= true
origin:                 --onion--tomato--onion-> onlyOnions=false
   new:                               --tomato-> onlyOnions=false
origin:         --onion--tomato--tomato--onion-> onlyOnions=false
   new:                       --tomato--tomato-> onlyOnions=false
origin:  --onion--tomato--onion--tomato--onion-> onlyOnions=false
   new:                       --tomato--tomato-> onlyOnions=false

拆分寿司定义与针对寿司执行的操作

public class Main {
  static class OnlyOnionsV {
    Boolean forSkewer() { return true; }
    Boolean forOnion(Shish s) { return s.onlyOnions(); }
    Boolean forTomato(Shish s) { return false; }
  }

  static class RemOnionV {
    Shish forSkewer() { return new Skewer(); }
    Shish forOnion(Shish s) { return s.remOnion(); }
    Shish forTomato(Shish s) { return new Tomato(s.remOnion()); }
  }

  abstract static class Shish {
    OnlyOnionsV onlyOnionsFn = new OnlyOnionsV();
    RemOnionV remOnionFn = new RemOnionV();

    abstract Boolean onlyOnions();
    abstract Shish remOnion();
  }

  static class Skewer extends Shish {
    Boolean onlyOnions() { return onlyOnionsFn.forSkewer(); }
    Shish remOnion() { return remOnionFn.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    public Onion(Shish s) { this.s = s; }

    Boolean onlyOnions() { return onlyOnionsFn.forOnion(s); }
    Shish remOnion() { return remOnionFn.forOnion(s); }

    public String toString() { return "--onion" + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    Boolean onlyOnions() { return onlyOnionsFn.forTomato(s); }
    Shish remOnion() { return remOnionFn.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Onion(new Onion(new Skewer())),
        new Onion(new Tomato(new Onion(new Skewer()))),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer())))),
        new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer())))))
      }) {
      Shish onionRemoved = s.remOnion();
      System.out.println(String.format(
          "origin: %40s onlyOnions=%5s\n   new: %40s onlyOnions=%5s",
          s, s.onlyOnions(), onionRemoved, onionRemoved.onlyOnions()));
    }
  }
}
origin:                         --onion--onion-> onlyOnions= true
   new:                                       -> onlyOnions= true
origin:                 --onion--tomato--onion-> onlyOnions=false
   new:                               --tomato-> onlyOnions=false
origin:         --onion--tomato--tomato--onion-> onlyOnions=false
   new:                       --tomato--tomato-> onlyOnions=false
origin:  --onion--tomato--onion--tomato--onion-> onlyOnions=false
   new:                       --tomato--tomato-> onlyOnions=false

给寿司中的洋葱添加辣味

public class Main {
  static class OnlyOnionsV {
    Boolean forSkewer() { return true; }
    Boolean forOnion(Shish s, Boolean spicy) { return s.onlyOnions(); }
    Boolean forTomato(Shish s) { return false; }
  }

  static class RemOnionV {
    Shish forSkewer() { return new Skewer(); }
    Shish forOnion(Shish s, Boolean spicy) { return s.remOnion(); }
    Shish forTomato(Shish s) { return new Tomato(s.remOnion()); }
  }

  abstract static class Shish {
    OnlyOnionsV onlyOnionsFn = new OnlyOnionsV();
    RemOnionV remOnionFn = new RemOnionV();

    abstract Boolean onlyOnions();
    abstract Shish remOnion();
  }

  static class Skewer extends Shish {
    Boolean onlyOnions() { return onlyOnionsFn.forSkewer(); }
    Shish remOnion() { return remOnionFn.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    Boolean onlyOnions() { return onlyOnionsFn.forOnion(s, spicy); }
    Shish remOnion() { return remOnionFn.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    Boolean onlyOnions() { return onlyOnionsFn.forTomato(s); }
    Shish remOnion() { return remOnionFn.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Onion(new Onion(new Skewer(), true), false),
        new Onion(new Tomato(new Onion(new Skewer(), true)), false),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer(), true))), false),
        new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer(), true)), false)), true)
      }) {
      Shish onionRemoved = s.remOnion();
      System.out.println(String.format(
          "origin: %55s onlyOnions=%5s\n   new: %55s onlyOnions=%5s",
          s, s.onlyOnions(), onionRemoved, onionRemoved.onlyOnions()));
    }
  }
}
origin:                                  --onion--spicy-onion-> onlyOnions= true
   new:                                                      -> onlyOnions= true
origin:                          --onion--tomato--spicy-onion-> onlyOnions=false
   new:                                              --tomato-> onlyOnions=false
origin:                  --onion--tomato--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false
origin:     --spicy-onion--tomato--onion--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false

尽管没用到, forOnion 还是添加了 spicy 参数,这里其实不需要,但是如果想只移除辣味的洋葱,就需要了,考虑如何实现。另外,考虑我们的 forXXX 的参数的究竟是什么。

抽象重复代码

尝试:从类型定义移除对操作的依赖,将依赖放到方法参数中。

public class Main {
  static class OnlyOnionsV {
    Boolean forSkewer() { return true; }
    Boolean forOnion(Shish s, Boolean spicy) { return s.onlyOnions(this); }
    Boolean forTomato(Shish s) { return false; }
  }

  static class RemOnionV {
    Shish forSkewer() { return new Skewer(); }
    Shish forOnion(Shish s, Boolean spicy) { return s.remOnion(this); }
    Shish forTomato(Shish s) { return new Tomato(s.remOnion(this)); }
  }

  abstract static class Shish {
    abstract Boolean onlyOnions(OnlyOnionsV onlyOnionsFn);
    abstract Shish remOnion(RemOnionV remOnionFn);
  }

  static class Skewer extends Shish {
    Boolean onlyOnions(OnlyOnionsV onlyOnionsFn) { return onlyOnionsFn.forSkewer(); }
    Shish remOnion(RemOnionV remOnionFn) { return remOnionFn.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    Boolean onlyOnions(OnlyOnionsV onlyOnionsFn) { return onlyOnionsFn.forOnion(s, spicy); }
    Shish remOnion(RemOnionV remOnionFn) { return remOnionFn.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    Boolean onlyOnions(OnlyOnionsV onlyOnionsFn) { return onlyOnionsFn.forTomato(s); }
    Shish remOnion(RemOnionV remOnionFn) { return remOnionFn.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Onion(new Onion(new Skewer(), true), false),
        new Onion(new Tomato(new Onion(new Skewer(), true)), false),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer(), true))), false),
        new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer(), true)), false)), true)
      }) {
      Shish onionRemoved = s.remOnion(new RemOnionV());
      System.out.println(String.format(
          "origin: %55s onlyOnions=%5s\n   new: %55s onlyOnions=%5s",
          s, s.onlyOnions(new OnlyOnionsV()),
          onionRemoved, onionRemoved.onlyOnions(new OnlyOnionsV())));
    }
  }
}
origin:                                  --onion--spicy-onion-> onlyOnions= true
   new:                                                      -> onlyOnions= true
origin:                          --onion--tomato--spicy-onion-> onlyOnions=false
   new:                                              --tomato-> onlyOnions=false
origin:                  --onion--tomato--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false
origin:     --spicy-onion--tomato--onion--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false

两个问题

  • OnlyOnionsV 和 RemOnionV 长得明显一样。
  • 我们还是依赖具体实现,考虑如何移除一片洋葱。

仅从形式上也能从 OnlyOnionsVRemOnionV 抽象出 ShishVisitor:

interface ShishVisitor<T> {
  T forSkewer();
  T forOnion(Shish s, Boolean spicy);
  T forTomato(Shish s);
}

static class OnlyOnionsV implements ShishVisitor<Boolean> {
  public Boolean forSkewer() { return true; }
  public Boolean forOnion(Shish s, Boolean spicy) { return s.onlyOnions(this); }
  public Boolean forTomato(Shish s) { return false; }
}

static class RemOnionV implements ShishVisitor<Shish> {
  public Shish forSkewer() { return new Skewer(); }
  public Shish forOnion(Shish s, Boolean spicy) { return s.remOnion(this); }
  public Shish forTomato(Shish s) { return new Tomato(s.remOnion(this)); }
}

考虑为什么把抽象出来的东西叫 Visitor, 谁访问谁。

接受访问:

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  static class RemOnionV implements ShishVisitor<Shish> {
    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    for (Shish s: new Shish[]{
        new Onion(new Onion(new Skewer(), true), false),
        new Onion(new Tomato(new Onion(new Skewer(), true)), false),
        new Onion(new Tomato(new Tomato(new Onion(new Skewer(), true))), false),
        new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer(), true)), false)), true)
      }) {
      Shish onionRemoved = s.accept(new RemOnionV());
      System.out.println(String.format(
          "origin: %55s onlyOnions=%5s\n   new: %55s onlyOnions=%5s",
          s, s.accept(new OnlyOnionsV()),
          onionRemoved, onionRemoved.accept(new OnlyOnionsV())));
    }
  }
}
origin:                                  --onion--spicy-onion-> onlyOnions= true
   new:                                                      -> onlyOnions= true
origin:                          --onion--tomato--spicy-onion-> onlyOnions=false
   new:                                              --tomato-> onlyOnions=false
origin:                  --onion--tomato--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false
origin:     --spicy-onion--tomato--onion--tomato--spicy-onion-> onlyOnions=false
   new:                                      --tomato--tomato-> onlyOnions=false

移除指定风味的洋葱:

static class RemOnionOf implements ShishVisitor<Shish> {
  Boolean spicy;
  RemOnionOf(Boolean spicy) { this.spicy = spicy; }

  public Shish forSkewer() { return new Skewer(); }
  public Shish forOnion(Shish s, Boolean spicy) {
    if (this.spicy == spicy) {
      return s.accept(this);
    } else {
      return new Onion(s.accept(this), spicy);
    }
  }
  public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
}

移除指定数量的洋葱:

static class RemSomeOnion implements ShishVisitor<Shish> {
  int count;
  RemSomeOnion(int count) { this.count = count; }

  public Shish forSkewer() { return new Skewer(); }
  public Shish forOnion(Shish s, Boolean spicy) {
    if (count > 0) {
      return s.accept(new RemSomeOnion(count - 1));
    } else {
      return new Onion(s.accept(this), spicy);
    }
  }
  public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
}

综上

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class RemOnionOf implements ShishVisitor<Shish> {
    Boolean spicy;
    RemOnionOf(Boolean spicy) { this.spicy = spicy; }

    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) {
      if (this.spicy == spicy) {
        return s.accept(this);
      } else {
        return new Onion(s.accept(this), spicy);
      }
    }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  static class RemSomeOnion implements ShishVisitor<Shish> {
    int count;
    RemSomeOnion(int count) { this.count = count; }

    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) {
      if (count > 0) {
        return s.accept(new RemSomeOnion(count - 1));
      } else {
        return new Onion(s.accept(this), spicy);
      }
    }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  static class RemOnionV implements ShishVisitor<Shish> {
    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    Shish s = new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer(), true)), false)), true);
    System.out.println(String.format("Origin                 : %s", s));
    System.out.println(String.format("accept RemOnionV       : %s", s.accept(new RemOnionV())));
    System.out.println(String.format("accept RemOnionOf spicy: %s", s.accept(new RemOnionOf(true))));
    System.out.println(String.format("accept RemSomeonion 1  : %s", s.accept(new RemSomeOnion(1))));
  }
}
Origin                 : --spicy-onion--tomato--onion--tomato--spicy-onion->
accept RemOnionV       : --tomato--tomato->
accept RemOnionOf spicy: --tomato--onion--tomato->
accept RemSomeonion 1  : --tomato--onion--tomato--spicy-onion->

考虑移除指定数量指定风味的洋葱。

给寿司里添加点荤食

我们现在有:

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    Shish s = new Onion(new Tomato(new Onion(new Tomato(new Onion(new Skewer(), true)), false)), true);
    System.out.println(s);
  }
}
--spicy-onion--tomato--onion--tomato--spicy-onion->

考虑如何不改变现有代码,扩展添加荤食 (Lamb)?

添加 Lamb

class Lamb extends Shish {
  Shish s;
  Lamb(Shish s) { this.s = s; }

  <T> T accept(ShishVisitor<T> ask) { return ask.???; }
}

受阻,现有类型 ShishVisitor<T> 不能对 Lamb 处理。显然 Lamb 接受的访问者需要认识它。添加 LambShishVisitor<T> :

interface LambShishVisitor<T> extends ShishVisitor<T> {
  T forLamb(Shish s);
}

回来看 Lamb:

class Lamb extends Shish {
  Shish s;
  Lamb(Shish s) { this.s = s; }

  <T> T accept(ShishVisitor<T> ask) { return ((LambShishVisitor) ask).forLamb(s); }

  public String toString() { return "--lamb" + s; }
}

这里丑陋的使用了类型强制转换,意味着如果不小心传入的不是 LambShishVisitor 运行时将报类型转换错误。事实上我们来考虑扩展实现已有的 OnlyOnionsV:

class LambOnlyOnionsV extends OnlyOnionsV implements LambShishVisitor<Boolean> {
  public Boolean forLamb() { return false; }
}

但似乎没什么问题了:

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  // Lamb extension
  interface LambShishVisitor<T> extends ShishVisitor<T> {
    T forLamb(Shish s);
  }

  static class Lamb extends Shish {
    Shish s;
    Lamb(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ((LambShishVisitor<T>) ask).forLamb(s); }

    public String toString() { return "--lamb" + s; }
  }

  static class LambOnlyOnionsV extends OnlyOnionsV implements LambShishVisitor<Boolean> {
    public Boolean forLamb(Shish s) { return false; }
  }

  public static void main(String[] args) {
    Shish s1 = new Lamb(new Skewer());
    Shish s2 = new Onion(new Lamb(new Skewer()), true);
    // System.out.println(String.format(
    //     "%s onlyOnions=%s", s1, s1.accept(new LambOnlyOnionsV())));
    System.out.println(String.format(
        "%s onlyOnions=%s", s2, s2.accept(new LambOnlyOnionsV())));
  }
}
--spicy-onion--lamb-> onlyOnions=false

考虑上面说的类型强制转换还可能在什么地方导致问题。

一点小问题

现在要移除一片洋葱要怎么做?

似乎很简单:

static class LambRemSomeOnion extends RemSomeOnion implements LambShishVisitor<Shish> {
  LambRemSomeOnion(int count) { super(count); }

  public Shish forLamb(Shish s) { return new Lamb(s.accept(this)); }
}

考虑 new Onion(new Lamb(new Onion(new Skewer(), false)), false).accept(new LambRemSomeOnion(1))

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  static class RemSomeOnion implements ShishVisitor<Shish> {
    int count;
    RemSomeOnion(int count) { this.count = count; }

    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) {
      if (count > 0) {
        return s.accept(new RemSomeOnion(count - 1));
      } else {
        return new Onion(s.accept(this), spicy);
      }
    }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  // Lamb extension
  interface LambShishVisitor<T> extends ShishVisitor<T> {
    T forLamb(Shish s);
  }

  static class Lamb extends Shish {
    Shish s;
    Lamb(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ((LambShishVisitor<T>) ask).forLamb(s); }

    public String toString() { return "--lamb" + s; }
  }

  static class LambOnlyOnionsV extends OnlyOnionsV implements LambShishVisitor<Boolean> {
    public Boolean forLamb(Shish s) { return false; }
  }

  static class LambRemSomeOnion extends RemSomeOnion implements LambShishVisitor<Shish> {
    LambRemSomeOnion(int count) { super(count); }

    public Shish forLamb(Shish s) { return new Lamb(s.accept(this)); }
  }

  public static void main(String[] args) {
    Shish s = new Onion(new Lamb(new Onion(new Skewer(), false)), false);
    System.out.println(String.format("Origin: %s", s));
    System.out.println(String.format("Rem 1 onion: %s", s.accept(new LambRemSomeOnion(1))));
  }
}

Exception in thread "main" java.lang.ClassCastException: Main$RemSomeOnion cannot be cast to Main$LambShishVisitor at Main$Lamb.accept(Main.java:73) at Main$RemSomeOnion.forOnion(Main.java:27) at Main$RemSomeOnion.forOnion(Main.java:20) at Main$Onion.accept(Main.java:50) at Main.main(Main.java:5)

因为在 RemSomeOnion 实现中 forOnion 中使用了 new RemSomeOnion(count - 1). 它导致扩展出现问题。修正它,给该 Visitor 的扩展留出空间。

static class RemSomeOnion implements ShishVisitor<Shish> {
  int count;
  RemSomeOnion(int count) { this.count = count; }
  public RemSomeOnion newRemSomeOnion(int count) { return new RemSomeOnion(count); }

  public Shish forSkewer() { return new Skewer(); }
  public Shish forOnion(Shish s, Boolean spicy) {
    if (count > 0) {
      return s.accept(newRemSomeOnion(count - 1));
    } else {
      return new Onion(s.accept(this), spicy);
    }
  }
  public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
}

static class LambRemSomeOnion extends RemSomeOnion implements LambShishVisitor<Shish> {
  LambRemSomeOnion(int count) { super(count); }
  public LambRemSomeOnion newRemSomeOnion(int count) { return new LambRemSomeOnion(count); }

  public Shish forLamb(Shish s) { return new Lamb(s.accept(this)); }
}

最终:

public class Main {
  interface ShishVisitor<T> {
    T forSkewer();
    T forOnion(Shish s, Boolean spicy);
    T forTomato(Shish s);
  }

  static class OnlyOnionsV implements ShishVisitor<Boolean> {
    public Boolean forSkewer() { return true; }
    public Boolean forOnion(Shish s, Boolean spicy) { return s.accept(this); }
    public Boolean forTomato(Shish s) { return false; }
  }

  static class RemSomeOnion implements ShishVisitor<Shish> {
    int count;
    RemSomeOnion(int count) { this.count = count; }
    public RemSomeOnion newRemSomeOnion(int count) { return new RemSomeOnion(count); }

    public Shish forSkewer() { return new Skewer(); }
    public Shish forOnion(Shish s, Boolean spicy) {
      if (count > 0) {
        return s.accept(newRemSomeOnion(count - 1));
      } else {
        return new Onion(s.accept(this), spicy);
      }
    }
    public Shish forTomato(Shish s) { return new Tomato(s.accept(this)); }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(s, spicy); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(s); }

    public String toString() { return "--tomato" + s; }
  }

  // Lamb extension
  interface LambShishVisitor<T> extends ShishVisitor<T> {
    T forLamb(Shish s);
  }

  static class Lamb extends Shish {
    Shish s;
    Lamb(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ((LambShishVisitor<T>) ask).forLamb(s); }

    public String toString() { return "--lamb" + s; }
  }

  static class LambOnlyOnionsV extends OnlyOnionsV implements LambShishVisitor<Boolean> {
    public Boolean forLamb(Shish s) { return false; }
  }

  static class LambRemSomeOnion extends RemSomeOnion implements LambShishVisitor<Shish> {
    LambRemSomeOnion(int count) { super(count); }
    public LambRemSomeOnion newRemSomeOnion(int count) { return new LambRemSomeOnion(count); }

    public Shish forLamb(Shish s) { return new Lamb(s.accept(this)); }
  }

  public static void main(String[] args) {
    Shish s = new Onion(new Lamb(new Onion(new Skewer(), false)), false);
    System.out.println(String.format("Origin: %s", s));
    System.out.println(String.format("Rem 1 onion: %s", s.accept(new LambRemSomeOnion(1))));
  }
}
Origin: --onion--lamb--onion->
Rem 1 onion: --lamb--onion->

清洗掉寿司中有辣味的洋葱上的辣味

public class Main {
  interface ShishVisitor<T> {
    T forSkewer(Skewer that);
    T forOnion(Onion that);
    T forTomato(Tomato that);
  }

  static class WashOnionV implements ShishVisitor<Shish> {
    public Shish forSkewer(Skewer that) { return that; }
    public Shish forOnion(Onion that) {
      if (that.spicy) {
        that.spicy = false;
      }
      that.s.accept(this);
      return that;
    }
    public Shish forTomato(Tomato that) {
      that.s.accept(this);
      return that;
    }
  }

  abstract static class Shish {
    abstract <T> T accept(ShishVisitor<T> ask);
  }

  static class Skewer extends Shish {
    <T> T accept(ShishVisitor<T> ask) { return ask.forSkewer(this); }

    public String toString() { return "->"; }
  }

  static class Onion extends Shish {
    Shish s;
    Boolean spicy;
    public Onion(Shish s, Boolean spicy) { this.s = s; this.spicy = spicy; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forOnion(this); }

    public String toString() { return (spicy ? "--spicy-onion" : "--onion" ) + s; }
  }

  static class Tomato extends Shish {
    Shish s;
    public Tomato(Shish s) { this.s = s; }

    <T> T accept(ShishVisitor<T> ask) { return ask.forTomato(this); }

    public String toString() { return "--tomato" + s; }
  }

  public static void main(String[] args) {
    Shish s = new Onion(new Tomato(new Onion(new Skewer(), true)), true);
    System.out.println(String.format("             Origin: %s", s));
    s.accept(new WashOnionV());
    System.out.println(String.format("After Washing Onion: %s", s));
  }
}
             Origin: --spicy-onion--tomato--spicy-onion->
After Washing Onion: --onion--tomato--onion->

考虑这里 forXXX 的参数又是什么,和之前有什么区别。

Advices

The First Bit of Advice

When specifying a collection of data, use abstract classes for data types and extended classes for variants.

The Second Bit of Advice

Whe writing a function over a datatype, place a method in each of the variants that make up the datatype. If a field of a variant belongs to the same datatype, the method may call the corresponding method of the field in computing the function.

The Third Bit of Advice

When writing a function that returns values of a datatype, use new to create these values.

The Fourth Bit of Advice

When writing several functions for the same self-referential datatype, use visitor protocols so that all methods for a function can be found in a single class.

The Sixth Bit of Advice

When the additional consumed values change for a self-referential use of a visitor, don't forget to create a new visitor.

The Seventh Bit of Advice

When designing visitor protocols for many different types, create a unifying protocol using Object.

The Eighth Bit of Advice

When extending a class, use overriding to enrich its functionality.

The Ninth Bit of Advice

If a datatype may have to be extended, be forward looking and use a constructor like (overridable) method so that visitors can be extended, too.

The Tenth Bit of Advice

When modifications to objects are needed, use a class to insulate the operations that modify objects. Otherwise, beware the consequences of your actions.

Author: lotuc, Published at: 2019-04-10 Wed 00:00, Modified At: 2023-04-02 Sun 22:39 (orgmode - Publishing)