在談設計模式的過程中,我就假定讀者已經熟知物件導向的四大特色了,若有不熟之處,可能在麻煩先看看手邊的OO書籍,或者我先前所寫的物件導向相關文章(抽象化、封裝、繼承、多行)。所謂策略模式其實就是把『變動的部分淬取出來,加以封裝』。有何好處呢?其一,使得往後在執行過程可以彈性使用之;其二,之後的演算法變動、擴充、增減對外界的影響不大。
策略模式:說穿了還是封裝,只是封裝的更有設計感。其實採用策略模式就是希望在日後的維護能更有彈性,調整時對其他類別的影響幅度竟可能降低。
日常中我們總是會犯下列的設計錯誤:『愛好繼承』。如果上過物件導向的課程,應該很常聽到程式再利用,如何做呢?當然就是用繼承囉!其實,繼承要小心使用,否則日後的維護會有越來越多問題產生。舉一例子來看:
class Animal {
public void walk(){}
public void fly(){}
}
class Dog extends Animal {
public void walk(){“Dog walk”;}
public void fly(){“don’t fly”; }
}
class Person extends Animal {
public void walk(){“Person walk”;}
public void fly(){“don’t fly”;}
}
class Bird extends Animal {
public void walk(){“don’t walk”;}
public void fly(){“bird fly”;}
}
上面是一個不好的繼承寫法,這種寫法會使得,明明不會飛得動物:Dog、Person等,一定要去把fly() override,因為如果忘了override,這樣Dog即可高飛,那天下不就亂了。
針對上面的亂象開始有人會想到:『介面』這個彈性的解決方案。我們只要把fly跟walk變成兩個介面,那需要的自行去實做,感覺天下就太平了,所以程式將再度轉變如下:
interface Walker {
public void walk();
}
interface Flyer {
public void fly();
}
class Person implements Walker {
public void walk() {“Person walk”;}
}
class Dog implements Walker {
public void walk(){“Dog walk”;}
}
Class Bird implements Flyer {
Public void fly() {“Bird fly”;}
}
上面就是採用介面的撰寫方式,這種方法雖然不像繼承這樣,會受到父類別的擴充而干擾,但卻會引發另外一個議題,那就是『失去了程式再利用的特質』,如果有一百個子類別,我想光override就寫到瘋了。
真正好的解法是善用合成(composition or aggregation),將上述的介面利用一類別將之引入。而各個介面自己有各自不同的實做方式,供日後其它的類別去做挑選。一方面,介面的實做方法的改變不會影響到外界的使用方式;另一方面針對不會改變的程式部分,可以依舊採用繼承的方式來實做。其修改後的程式如下:
interface Walker {
public void walk();
}
class TwoFootWalk implements Walker{
public void walk(){“two foots walking”;}
}
class FourFootWalk implements Walker{
public void walk(){“four foots walking”;}
}
-------------------------------------------------------------------------------
interface Flyer {
public void fly();
}
class WingFlyer implements{
public void fly(){“wing flying”;}
}
class NoFlyer implements{
public void fly(){“no flying”;}
}
-------------------------------------------------------------------------------
將上述的介面合成在Animal的類別中使用(亦即當成屬性來操作,日後即可用此介面來間接操作,它底下不同實做類別的物件,也就是多型的應用囉!)。
class Animai {
//會變化的部分利用介面來接收之,後續用多型進行操作。下面的Walker跟Flyer皆為介面,
//代表動物會有這兩個介面組合而成。此及對會變動的部分:『多用合成、少用繼承』的概念。
public Walker walker;
public Flyer flyer;
//others common methods
… //各動物會用到不會變得寫在此,供後續的子類別使用。
}
class Person {
public Person() {
walker = new TwoFootWalk();//在此決定person走路的方式
flyer = new NoFlyer();
}
}
class Dog {
Public Dog(){
walker = new FourFootWalk(); //在此決定dog走路的方式
flyer = new NoFlyer();
}
}
依照上述這樣的作法,會使得Person、Dog等依舊可以繼承Animal此一類別,而針對子類別之間有各自不同的部分,又可以自主決定該用哪些。如此Person跟Dog就不用在針對fly()這個方法作處理,也不用再顧慮會被父類別的方法影響。將來在撰寫主程式時將可以達到:針對介面寫程式,不針對具象類別寫程式的好處。例如:
Animal a = getAnimal(); //取得一個實體動物,可能為Person、Dog、Bird
a.walker.walk();
a.flyer.fly();
可以看到上面的運作都沒有改變。
留言列表