小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來實現要簡潔許多。”