GitHub源碼分享
微信搜索:碼農StayUp
主頁地址:https://gozhuyinglong.github.io
源碼分享:https://github.com/gozhuyinglong/blog-demos
1. 一個簡單的模擬鴨子游戲
我們先來看一個模擬鴨子的游戲:游戲中會出現各種鴨子,它們一邊游泳戲水,一邊呱呱叫。
經過一番調研后:
已知的鴨子種類有:野鴨(Mallard Duck)、紅頭鴨(Redhead Duck)、橡皮鴨(Rubber Duck)。
已知的鴨子行為有:游泳(Swim)、嘎嘎叫(Quack)、顯示鴨子的樣子(Display)。
下面是這些鴨子的外觀:
需求明確,開搞!
1.1 是時候展示OO技術了~
為了可復用性,設計了一個鴨子超類Duck
,并讓各種鴨子繼承此超類:
- 該超類中實現了
swim()
、quack()
方法,由于每一種鴨子外觀不同,所以display()
指定為抽象方法(當然該類也是抽象類)。 - 各個子類具體實現了
display()
方法 - 由于橡皮鴨不會“嗄嗄叫”,所以重寫了
quack()
方法為“吱吱叫”。
下面是UML類圖:
1.2 Change!!!
我們知道軟件開發的一個不變的真理是:“變化”!
現在要求增加一種鴨子行為:飛行(Fly),該怎么做呢?
如果繼續使用繼承,那么橡皮鴨是不會飛行的,還是需要重寫fly()
方法。如下:
那如果再增加一種鴨子:誘餌鴨(Decoy Duck),這是只木頭鴨子,它即不會叫,也不會飛行......
看來繼承是不能滿足需求了!
1.3 使用接口怎么樣?
將fly()
方法和quack()
抽離出來,做成接口,讓擁有該行為的鴨子對其進行實現。如下:
這似乎解決了現在的問題!
但如果再增加100只鴨子呢?豈不是所有會飛行或會叫的鴨子,都要實現一遍,沒有達到代碼的復用性。而且一旦要修改某個行為將是一件痛苦的事(比如將所有“吱吱叫”改成“仿真呱呱叫”)……
1.4 封裝變化
有一個設計原則,恰好適用于上面模擬鴨子游戲的狀況。
找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。
換句話說,如果每次來新的需求,都會使某方面的代碼發生變化,那么你就可以確定,這部分的代碼需要被抽出來,和其他穩定的代碼有所區分。
這便是策略模式的精神所在,下面我們來看該模式的詳細介紹。
2. 策略模式
策略模式(Strategy Pattern)是一種行為型模式。該模式定義一系列的算法,把它們一個個封裝起來,并使它們可以相互替換。該模式讓算法的變化獨立于使用它的客戶。
Define a family of algorithms, encapsulate each one, and make them interchangeable.
該設計模式體現了幾個設計原則:
- 封裝變化
- 針對接口編程,而不是實現類
- 多用組合,少用繼承
策略模式由三部分組成:
- Strategy(策略)
定義了所有策略的公共接口。上下文(Context)使用這個接口來調用某個具體策略(ConcreteStrategy)。 - ConcreteStrategy(具體策略)
Strategy接口的實現,定義了一個具體的策略實現。 - Context(上下文)
定義了Strategy對象如何來使用,是策略算法的調用者。
3. 代碼實現
我們使用策略模式實現上面模擬鴨子游戲。
3.1 飛行行為實現
定義飛行行為接口
public interface Fly {
void fly();
}
用翅膀飛行實現類
public class FlyWithWings implements Fly {
@Override
public void fly() {
System.out.println("用翅膀飛行");
}
}
不會飛行實現類
public class FlyNoWay implements Fly {
@Override
public void fly() {
System.out.println("不會飛行");
}
}
3.2 鴨叫行為實現
定義鴨叫行為接口
public interface Quack {
void quack();
}
呱呱叫實現類
public class QuackGuaGua implements Quack {
@Override
public void quack() {
System.out.println("呱呱叫");
}
}
吱吱叫實現類
public class QuackZhiZhi implements Quack {
@Override
public void quack() {
System.out.println("吱吱叫");
}
}
不會叫實現類
public class QuackNoWay implements Quack {
@Override
public void quack() {
System.out.println("不會叫");
}
}
3.3 鴨子類的實現
定義鴨子抽象類
public abstract class Duck {
protected Fly fly;
protected Quack quack;
public void swim() {
System.out.println("正在游泳...");
}
public abstract void display();
public Fly getFly() {
return fly;
}
public Quack getQuack() {
return quack;
}
}
野鴨實現類
public class MallardDuck extends Duck {
// 野鴨用翅膀飛行,呱呱叫
public MallardDuck() {
this.fly = new FlyWithWings();
this.quack = new QuackGuaGua();
}
@Override
public void display() {
System.out.println("外觀是綠頭鴨");
}
}
紅頭鴨實現類
public class RedheadDuck extends Duck {
// 紅頭鴨用翅膀飛行,呱呱叫
public RedheadDuck() {
this.fly = new FlyWithWings();
this.quack = new QuackGuaGua();
}
@Override
public void display() {
System.out.println("外觀是紅頭鴨");
}
}
橡皮鴨實現類
public class RubberDuck extends Duck {
// 橡皮鴨不會飛行,吱吱叫
public RubberDuck() {
this.fly = new FlyNoWay();
this.quack = new QuackZhiZhi();
}
@Override
public void display() {
System.out.println("外觀是橡皮鴨");
}
}
誘餌鴨實現類
public class DecoyDuck extends Duck {
// 誘餌鴨不會飛行,也不會叫
public DecoyDuck() {
this.fly = new FlyNoWay();
this.quack = new QuackNoWay();
}
@Override
public void display() {
System.out.println("外觀是誘餌鴨");
}
}
3.4 測試
編寫簡單測試類
public class Test {
public static void main(String[] args) {
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.display();
mallardDuck.swim();
mallardDuck.getFly().fly();
mallardDuck.getQuack().quack();
System.out.println("-------------------");
DecoyDuck decoyDuck = new DecoyDuck();
decoyDuck.display();
decoyDuck.swim();
decoyDuck.getFly().fly();
decoyDuck.getQuack().quack();
}
}
輸出結果
外觀是綠頭鴨
正在游泳...
用翅膀飛行
呱呱叫
-------------------
外觀是誘餌鴨
正在游泳...
不會飛行
不會叫
4. 完整代碼
完整代碼請訪問我的Github,若對你有幫助,歡迎給個Star,謝謝!
5. 參考
- 《 Head First 設計模式 》
- 《 設計模式:可復用面向對象軟件的基礎 》