Strategy Pattern 策略模式

最近在學設計模式,這些博客權當筆記。

Strategy Pattern 策略模式

策略模式簡介:定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立於使用算法的客戶。

現在有一個需求,有一個Duck鴨子的父類,需要用父類來生出很多不同種類的鴨子子類,大家都知道真實的鴨子會叫,有的鴨子會飛,(我也不知道會不會飛),但是還有玩具鴨子,例如橡皮鴨子(一捏就會吱吱叫,但是不會飛)、木頭鴨子(怎麼捏也不會叫,也不會飛),還有唐老鴨(唐老鴨揹着噴氣筒用火箭飛,還會呱呱叫)。這只是暫時的需求,後期還會根據新生的鴨子產生對應的需求。

這種需求是很簡單的需求,只需要有一個Duck父類,在父類裏設計一個fly和一個quack方法,讓子類來繼承它就哦了。如果Duck父類實現了fly(用翅膀飛)和quack(呱呱叫)方法,那麼所有的鴨子子類都會繼承這兩個方法,代碼簡介了很多,而且子類可以通過覆蓋這兩個方法去實現不同的功能。
但是如果有橡皮鴨子和木頭鴨子的出現,橡皮鴨子會吱吱叫而不是父類的呱呱叫,但是橡皮鴨子不會飛;木頭鴨子既不會叫也不會飛。
針對這種情況可以將父類的fly和quack方法設置成抽象方法,讓子類具體實現,這種方式有弊端,那就是如果有大量不同的子類就會造成在子類中fly和quack方法裏代碼的重複。
還有一種方法就是父類的fly和quack方法實現大多數鴨子的fly和quack功能,少數的鴨子子類只需要覆蓋這兩個方法即可,但是鬼知道大多數鴨子的fly和quack功能是什麼樣的、少數鴨子的fly和quack功能又是什麼樣的,並且在這個需求一直改變的現況下,用這兩種方式會經常去改原有的代碼。

我們可以使用策略模式來實現該功能,具體的操作就是把需求中可能會改變的東西給拿出來,讓它和不變的東西分離開來,這樣需求再改變的時候只需要修改可變的部分,而不用去修改原有的不變的代碼。代碼變化引起的不經意後果變少,系統變得更有彈性。這就是我們的設計原則:找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。意思就是,如果每次新的需求一來,都會使某方面的代碼發生變化,那麼你就可以確定,這部分代碼需要被抽出來,和其他穩定的代碼有所區分。這樣的概念很簡單,幾乎是每個設計模式背後的精神所在。所有的模式都提供一種方法讓"系統中的某部分改變不影響其他部分"。

我們知道Duck類的fly和quack方法會隨着鴨子的不同而有所改變,所以我們把這兩個方法從Duck類中抽出來,建立一組新類來代表每個行爲。我們希望一切能有彈性,因爲每個鴨子的實現類不確定,所以fly和quack方法的具體實現也不確定,我們就可以設計一個FlyBehavior和Quackehavior接口,然後每增加一種fly方式,我們就增加一個新類來實現FlyBehavior接口;同樣每增加一個quack方式,我們就增加一個新類類實現QuackBehavior接口。這樣就可以動態的去實現fly和quack方法,而不需要在編譯期寫死。



<span style="font-size:18px;">package wang.behavior;

public interface FlyBehavior {
	void fly();
}</span>
<span style="font-size:18px;">package wang.behavior;


public interface QuackBehavior {
<span style="white-space:pre">	</span>void quack();
}
</span>

這裏如果有一個真實的鴨子,那麼就增加一個RealFly類來實現FlyBehavior接口同時實現fly方法,因爲父類Duck已經有了FlyBehavior的引用,所以可以在父類Duck的fly方法裏直接調用FlyBehavior的接口,而在子類RealDuck中的構造方法中來根據實際情況去實例化一個FlyBehavior的子類,這裏實例化的是RealFly。這樣再調用RealDuck的fly方法的時候就會調用RealFly的fly方法。這樣就做到了動態實現fly方法。這就是另外一個設計原則:針對接口編程,而不是針對實現編程。
package wang.oldservice;

/**
 * @author Wang
 */
public abstract class Duck {
	//抽象方法,讓子類去實現自己要展示的是什麼
	public abstract void display();
	/**
	 * 所有的鴨子子類都會繼承該方法,都有飛的功能
	 */
	public void fly(){
		System.out.println("I am Flying~");
	}
	
	/**
	 * 所有的鴨子子類都會繼承該方法,都有呱呱叫的功能
	 */
	public void quack(){
		System.out.println("呱呱呱~~");
	}
}

這樣的設計,可以讓fly和quack方法被其他鴨子的實現類複用,因爲這兩個行爲已經不再和鴨子類有關了。 另外我們還可以再增加些其他的行爲,例如eat(吃,不同鴨子吃的行爲也不一樣),這樣不會影響到即有的行爲類,也不會影響"使用"到fly行爲的鴨子類。這麼一來,有了繼承的"複用"好處,卻沒有繼承帶來的包袱。這些實現了FlyBehavior和QuackBehavior接口的"行爲類"(因爲這些類中只有行爲,沒有具體的屬性)不但可以用在鴨子上,也可以用在其他類上,如雞、鳥、天鵝...

package wang.behavior.impl;

import wang.behavior.FlyBehavior;

public class FlyWithWings implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("我用翅膀飛....");
	}

}
<pre name="code" class="java">不會飛的行爲:
package wang.behavior.impl;

import wang.behavior.FlyBehavior;

public class CannotFly implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("我不會飛...");
	}

}



用火箭飛行的唐老鴨:
package wang.behavior.impl;

import wang.behavior.FlyBehavior;

public class FlyWithRocket implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("我用火箭筒飛...");
	}

}
呱呱叫的行爲:
package wang.behavior.impl;

import wang.behavior.QuackBehavior;

public class GuaGuaQuack implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("呱呱 呱呱...");
	}

}
吱吱叫的行爲:
package wang.behavior.impl;

import wang.behavior.QuackBehavior;

public class ZhiZhiQuack implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("吱吱 吱吱...");
	}

}
不會叫的行爲:
package wang.behavior.impl;

import wang.behavior.QuackBehavior;

public class CannotQuack implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("<<我不會叫>>");
	}

}
接下來就是要實現具體的鴨子子類了
真是的鴨子:會呱呱叫,會用翅膀飛:
package wang.service;

import wang.behavior.impl.FlyWithWings;
import wang.behavior.impl.GuaGuaQuack;

public class RealDuck extends Duck {

	public RealDuck() {
		flyBehavior = new FlyWithWings();
		quackBehavior = new GuaGuaQuack();
	}

	@Override
	public void display() {
		System.out.println("我是一個真鴨子");
	}

}
橡皮鴨子,會吱吱叫,但是不會飛:
package wang.service;

import wang.behavior.impl.CannotFly;
import wang.behavior.impl.ZhiZhiQuack;

public class XiangpiDuck extends Duck {

	public XiangpiDuck() {
		flyBehavior = new CannotFly();
		quackBehavior = new ZhiZhiQuack();
	}

	@Override
	public void display() {
		System.out.println("我是一個橡皮鴨子...");
	}

}

木頭鴨子,不會飛,也不會叫:
package wang.service;

import wang.behavior.impl.CannotFly;
import wang.behavior.impl.CannotQuack;

public class WoodenDuck extends Duck {

	public WoodenDuck() {
		flyBehavior = new CannotFly();
		quackBehavior = new CannotQuack();
	}

	@Override
	public void display() {
		System.out.println("我是一個木頭鴨子");
	}

}
會用火箭飛的唐老鴨,也會呱呱叫:
package wang.service;

import wang.behavior.impl.FlyWithRocket;
import wang.behavior.impl.GuaGuaQuack;

public class DuckFlyWithRocket extends Duck {

	public DuckFlyWithRocket() {
		flyBehavior = new FlyWithRocket();
		quackBehavior = new GuaGuaQuack();
	}

	@Override
	public void display() {
		System.out.println("我會用火箭飛...");
	}

}

這樣我們就可以讓所有的鴨子子類任意組合fly和quack行爲,因爲fly和quack行爲是獨立的,誰都可以任意拿來用,只要遵循委託的規則。

測試:
package wang.behavior.test;

import wang.service.Duck;
import wang.service.DuckFlyWithRocket;
import wang.service.RealDuck;
import wang.service.WoodenDuck;
import wang.service.XiangpiDuck;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Duck duck1 = new RealDuck();
		duck1.display();
		duck1.fly();
		duck1.quack();

		System.out.println("----------------------------");

		Duck duck2 = new XiangpiDuck();
		duck2.display();
		duck2.fly();
		duck2.quack();

		System.out.println("----------------------------");

		Duck duck3 = new WoodenDuck();
		duck3.display();
		duck3.fly();
		duck3.quack();

		System.out.println("----------------------------");

		Duck duck4 = new DuckFlyWithRocket();
		duck4.display();
		duck4.fly();
		duck4.quack();
	}

}
輸出結果爲:
我是一個真鴨子
我用翅膀飛....
呱呱 呱呱...
----------------------------
我是一個橡皮鴨子...
我不會飛...
吱吱 吱吱...
----------------------------
我是一個木頭鴨子
我不會飛...
<<我不會叫>>
----------------------------
我會用火箭飛...
我用火箭筒飛...
呱呱 呱呱...








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章