小A:“模板方法模式有什么缺陷?”
大B:“组合优先于继承,而模板方法模式是少数几个必须从非纯虚类(C++的称呼,就是java的interface)继承来实现的模式之一。新手往往颇费周折才能理解其要义,因为它的复杂性——概念复杂性和实现复杂性。而用IOC模式代替继承才是降低复杂性的更好方案。1、概念复杂性。从面向对象方法的本质来讲,父类负责抽象,子类负责具体。而模板方法恰好反过来了,父类实现了大部分功能,而子类实现少数纯虚方法,然后由父类的方法调用。违反了面向对象的一般性思维的原因是这个模式的初衷并不是抽象,而是最大限度的重用。2、实现复杂性。这种重用方式的代价就是每个子类身上都背负了父类强加给它的包袱。”
举个例子:
publicabstractclassApplicationContext{
protectedStringpath;
publicabstractInputStreamgetStream();
publicvoidbuild(){
getStream();
}
publicApplicationContext(Stringpath){
this.path=path;
}
}
publicclassClassPathContextextendsApplicationContext{
publicClassPathContext(Stringpath){
//super(path)执行之前绝对不能使用path变量,因为父类还没有初始化。
super(path);//为了能够执行父类构建器,强加给子类的代码。
}
publicInputStreamgetStream(){……}
}
调用代码:
ApplicationContextcontext=newClassPathContext(“path”);
context.build();
大B:“这里父类带给子类的负担有两点:1、父类的非默认构建器子类必须重写。2、子类在构建器里使用父类的成员变量时要注意顺序。由此可见,这个模式在提高重用性的同时也增加了复杂性:1、子类的编写者必须了解父类的实现。2、父类的改动很容易波及到子类。用IOC组合方式进行就漂亮得多,把子类要实现的方法用interface来描述。这时两个父子关系的类就转变成调用与被调用的关系,之间通过interface形成契约。”
publicclassApplicationContext{
Readerreader;
publicvoidsetReader(Readerreader){this.reader=reader;}
publicvoidbuild(){
reader.getStream(path);
}
}
publicinterfaceReader{
publicInputStreamgetStream(Stringpath);
}
调用代码:
ApplicationContextcontext=newApplicationContext(“path”);
context.setReader(newReaderImpl());
context.build();
大B:“这两种实现最大的区别是ClassPathContext和ApplicationContext是紧耦合的。而ReaderImpl和ApplicaitonContext是正交的。其次就是IOC方式的实现代码要麻烦,这是java严谨的语言机制决定的,如果用C#的DelegateMethod来实现要简洁许多。”