場景闡述:通過設計一個超類鴨子而讓子類繼承,提取出部分公共方法進行實現,但後續添加需求時,因爲設計上的問題導致無窮無盡的噩夢……
事出有因:超類鴨子負責實現公共的呱呱叫(quack)方法,後續提出需求,想要爲鴨子加入fly方法以提高產品競爭力,但是由於技術失誤,將此fly方法作爲公共方法在超類鴨子中實現後,程序出現bug,橡皮鴨子也會飛啦!
改進策略:通過在橡皮鴨子中覆蓋fly方法,實現自己的獨立方法,但是這會導致每次新增一個鴨子子類時,都要考慮是否需要覆蓋fly方法,並且手動再編碼。
再改進:定義接口呢?提取出fly和quack,分別做成兩個接口,然後由每個鴨子子類去實現;缺點顯而易見,每個鴨子子類都要重寫方法,異常繁瑣,無窮無盡的噩夢。
設計原則:找出應用中可能需要變化之處,把他們獨立出來,不要和那些不需要變化的代碼混在一起。
設計原則:針對接口編程,而不是針對實現編程
“針對接口編程” 的真正意思是“針對超類型編程”。
“針對超類型編程”這句話,可以更明確地說成“變量地聲明類型應該是超類型,通常是一個抽象類或者是一個接口,如此,只要是具體實現此超類型的類所產生的對象,都可以指定給這個變量。這也意味着,聲明類時不用理會以後執行時的真正對象類型”。
通過上述原則,我們嘗試一種新的方式來進行設計。同樣是兩個接口,但是由其對應類來實現具體的行爲,將其和鴨子類分開。這就是可變化的部分。
我們看一下相應的代碼,來更好的理解這種模式。
首先建立一個鴨子抽象類
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void swim(){
System.out.println("All ducks float, even decoys");
}
}
然後爲分離出來的行爲新建兩個接口,及其實現類
public interface FlyBehavior {
public void fly();
}
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("I can't fly");
}
}
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("I'm flying!!");
}
}
public interface QuackBehavior {
public void quack();
}
public class Quack implements QuackBehavior{
@Override
public void quack() {
System.out.println("Quack");
}
}
然後根據需求新建一個鴨子子類
public class ModelDuck extends Duck{
public ModelDuck(){
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a model duck");
}
}
最後我們用一個測試類來測試一下
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck model = new ModelDuck();
model.performQuack();
model.performFly();
model.setFlyBehavior(new FlyWithWings());
model.performFly();
}
}
結果如下
當你將兩個類結合起來使用時,就是組合。
設計原則:多用組合,少用繼承。
使用組合建立系統具有很大的彈性,不僅可將算法族封裝成類,更可以“在運行時動態地改變行爲”,只要組合地行爲對象符合正確的接口標準即可。
策略模式:定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。
總結:該模式的核心思想是,分離出可變化的部分功能,然後通過一個算法族(也即一個接口及其的一系列實現類)來實現該功能,再將接口與原來的超類進行組合,每當需求發生變動的時候,新建子類中的接口實現類就可以根據需求進行相應的變動。從而實現了可伸縮的,彈性設計。