定義:定義了算法族,分别封裝起來,讓它們之間可以互相替換,此模式讓算法的變法獨立于使用算法的客戶。
關于模式的學習本來就是比較頭痛的事情,如果但看看理論。就算你知道了它應用的場景。在實際的應用中,你也很難的應用到你的項目中。了解容易,實際的應用更難。
接下來我要通過一個執行個體來講解這個設計模式。通過不斷的需求變化,來真正的展現設計模式帶來的好處,我們使用一些設計模式,就是讓你的程式可以應付客戶的不斷的需求變化。我們知道,在我們實際的開發中,難的不是技術上的問題,而是客戶随着開發的進行,他們的需求的不斷的變化,這是最頭痛的,如果你的程式不能對付客戶的變化的話,你就會付出更多的代價。為了讓我們的程式可以健壯,重用,可擴充。利用設計模式的思想去開發程式設計,可以解決上面的幾個問題。這并不是唯一的辦法,但是比較好的辦法。
我們的要求是設計一個模拟鴨子遊戲的應用程式。(注:這個應用是在Head First 設計模式 一書中的例子)。遊戲中會出現各種鴨子,一邊遊泳戲水,一邊呱呱叫。對于這樣的要求,我們首先會運用OO技術,并利用繼承的思想,設計一個鴨子的超類(Superclass),并讓各種鴨子繼承這個超類。
public class Duck{
public void quack(){ //呱呱叫
System.out.println("呱呱叫");
}
public void swim(){ //遊泳
System.out.println(" 遊泳");
}
public abstract void display(); /*因為外觀不一樣,讓子類自己去決定了。*/
}
對于它的子類隻需簡單的繼承就可以了,并實作自己的display()方法。
//野鴨
public class MallardDuck extends Duck{
public void display(){
System.out.println("野鴨的顔色...");
}
}
//紅頭鴨
public class RedheadDuck extends Duck{
System.out.println("紅頭鴨的顔色...");
}
這裡可能還會有其他顔色的鴨子。通過這個簡單的繼承我們滿足了客戶的要求。
不幸的是,現在客戶又提出了新的需求,想讓鴨子飛起來。這個對于我們OO程式員,在簡單不過了,在超類中在加一個方法就可以了。
public void fly(){
System.out.println("飛吧!鴨子");
}
在子類中隻需簡單的覆寫。
//殘廢鴨
public class DisabledDuck extends Duck{
System.out.println("殘廢鴨的顔色...");
//覆寫,變成什麼事都不做。
其它會飛的鴨子不用覆寫。
這樣所有的繼承這個超類的鴨子都會fly了。但是問題又出來了,客戶又提出有的鴨子會飛,有的不能飛。客戶就是那樣的讓我們頭痛啊,他們的需求會随着我們的開發而改變的。
這時我們需要做的是對會飛的鴨子重寫fly方法,對于不能飛的也重寫,但在方法裡什麼也不寫,隻是一個空方法,沒有任何實作。隻是簡單覆寫超類中的fly方法。這樣可以滿足暫時的需求了,但對我們的維護帶來了麻煩。對于一些不會叫的,也不會飛的鴨子的子類中,夾雜了一些沒有意義的代碼,就是對一些超類方法的覆寫。
對于上面的設計,你可能發現一些弊端,如果超類有新的特性,子類都必須變動,這是我們開發最不喜歡看到的,一個類變讓另一個類也跟着變,這有點不符合OO設計了。這樣很顯然的耦合了一起。
這時,我們會馬上想到運用接口來消除這種弊端。我們把容易引起變法的部分提取出來并封裝之,來應付以後的變法。雖然代碼量加大了,但可用性提高了,耦合度也降低了。同時也具有很強的擴充性,也真正的利用了OO設計思想。
我們把Duck中的fly方法和quack提取出來。
public interface Flyable{
public void fly();
public interface Quackable{
public void quack();
最後Duck的設計成為:
public abstract void display(); /*因為外觀不一樣,讓子類自 己去決定了。*/
而MallardDuck,RedheadDuck,DisabledDuck 就可以寫成為:
public class MallardDuck extends Duck implements Flyable,Quackable{
//實作該方法
public void quack(){
public class RedheadDuck extends Duck implements Flyable,Quackable{
//殘廢鴨 隻實作Quackable(能叫不能飛)
public class DisabledDuck extends Duck implements Quackable{
這樣已設計,我們的程式就降低了它們之間的耦合。
現在我們知道使用繼承并不能很好的解決問題,因為鴨子的行為在子類中不斷的變化,并且讓所用的子類都有這些行為是不恰當的。Flyable和Quackable接口一開始似乎還挺不錯的,解決了問題(隻有會飛到鴨子才實作 Flyable),但是Java接口不具有實作代碼,是以實作接口無法達到代碼的複用。這時我們有一個設計原則:找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。
現在我們根據這個設計原則,我們上面的設計還存在問題。我們因該如何解決呢?
現在,為了要分開“變化和不變化的部分”,我們準備建立兩組類(完全遠離Duck類),一個是"fly"相關的,另一個是“quack”相關的,每一組類将實作各自的動作。比方說,我們可能有一個類實作“呱呱叫”,另一個類實作“吱吱叫”,還有一個類實作“安靜”。
我們如何設計實作飛行和呱呱叫的行為的類呢?我們為了讓我們的程式有彈性,減小耦合。我們又必須遵循第二個設計原則:針對接口程式設計,而不是針對實作程式設計。
看看我們具體的實作吧,實作勝于理論。
首先寫兩個接口。FlyBehavior(飛行行為)和QuackBehavior(叫的行為).
public interface FlyBehavior{
public void fly();
public interface QuackBehavior{
我們在定義一些針對FlyBehavior的具體實作。
public class FlyWithWings implements FlyBehavior{
public void fly(){
//實作了所有有翅膀的鴨子飛行行為。
public class FlyNoWay implements FlyBehavior{
//什麼都不做,不會飛
}
}
針對QuackBehavior的幾種具體實作。
public class Quack implements QuackBehavior{
public void quack(){
//實作呱呱叫的鴨子
public class Squeak implements QuackBehavior{
//實作吱吱叫的鴨子
public class MuteQuack implements QuackBehavior{
//什麼都不做,不會叫
這樣的設計,可以讓飛行和呱呱叫的動作被其他的對象複用,因為這些行為已經與鴨子類無關了。而我們增加一些新的行為,不會影響到既有的行為類,也不會影響“使用”到飛行行為的鴨子類。
最後我們看看Duck 如何設計。
public class Duck{
FlyBehavior flyBehavior;//接口
QuackBehavior quackBehavior;//接口
public Duck(){}
public abstract void display();
public void swim(){
//實作遊泳的行為
}
public void performFly(){
flyBehavior.fly();//這時鴨子對象不親自處理飛行行為,而是委托給flyBehavior引用的對象。
}
public void performQuack(){
quackBehavior.quack();();//這時鴨子對象不親自處理叫的行為,而是委托給quackBehavior引用的對象。
看看MallardDuck如何實作。
public MallardDuck {
flyBehavior = new FlyWithWings ();
quackBehavior = new Quack();
//因為MallardDuck 繼承了Duck,所有具有flyBehavior 與quackBehavior 執行個體變量}
public void display(){
//實作
這樣就滿足了即可以飛,又可以叫,同時展現自己的顔色了。
這樣的設計我們可以看到是把flyBehavior ,quackBehavior 的執行個體化寫在子類了。我們還可以動态的來決定。
我們隻需在Duck中加上兩個方法。
public class Duck{
public void setFlyBehavior(FlyBehavior flyBehavior){
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior {
this.quackBehavior= quackBehavior;
大家看到這樣的方法應該知道它是利用依賴注入的思想,動态的來改變鴨子的行為。
在測試的類中,我們可以這樣寫。
public class Test{
public static void main(String[] args){
//一般的用法:
Duck mallard = new MallardDuck();
mallard.performFly();
mallard .perforeQuack();
//動态的改變
mallard.setFlyBehavior(new FlyRocketPowered);//具有火箭動力的飛行能力
mallard.performFly();
這樣的用法其實就是Spring 中的一個核心機制,Ioc依賴注入思想。
我們講了這麼些,其實就是政策模式的應用。我們寫了這麼多,就是為了讓你更加明白這個設計模式的應用場景。
政策模式:定義了算法族(飛行行為中的各種實作<看成一族算法>和呱呱叫行為中的各種實作<看成一族算法>),分别分裝起來,讓它們之間可以互相調用,此模式讓算法的變法(就是飛行行為和呱呱叫的行為)獨立于使用算法的客戶。
本文轉自 weijie@java 51CTO部落格,原文連結:http://blog.51cto.com/weijie/74807,如需轉載請自行聯系原作者