在談設計模式的過程中,我就假定讀者已經熟知物件導向的四大特色了,若有不熟之處,可能在麻煩先看看手邊的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();

可以看到上面的運作都沒有改變。

arrow
arrow
    全站熱搜

    劉逸 發表在 痞客邦 留言(0) 人氣()