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 长得明显一样。
- 我们还是依赖具体实现,考虑如何移除一片洋葱。
仅从形式上也能从 OnlyOnionsV 和
RemOnionV 抽象出 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 extensioninterface 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 extensioninterface 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 extensioninterface 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.