Java 設計模式之策略模式

要想寫出可維護、可複用、可擴展的應用程序,就必須掌握一些設計模式的思想.設計模式重在設計,是一種思想層面的東西,需要細細的去領悟。今天學習了一下設計模式的第一個模式——策略模式,現在把它整理出來。

策略模式:定義了算法族,分別封裝起來,讓他們之間可以互相轉換,此模式讓算法的變化獨立於使用算法的用戶。初次去看這麼一個定義,估計很多人跟我一樣,直接暈掉或者崩潰掉。完全不理解,沒關係,我們由一個例子來逐步分析策略模式。

這是一個關於鴨子類的遊戲,在這個程序中,會有各種各樣的鴨子在水裏游泳,呱呱叫等。我們來看代碼實現.

<span style="font-family:Courier New;font-size:14px;">/*
 *  鴨子的抽象類 我們定義了一個鴨子超類,他擁有鴨子共同特徵的方法,比如呱呱叫,並且擁有一個抽象的方法,因爲鴨子有各種各樣不同的外觀
 */
public abstract class Duck {
	//鴨子呱呱叫
	public void quack() {
		System.out.println("呱呱...");
	}
	//游泳行爲
	public void swim() {
		System.out.println("我可以在水裏游泳");
	}
	//鴨子外觀的抽象方法 
	public abstract void display();
}</span>

<span style="font-family:Courier New;font-size:14px;">/*
 *  外觀是綠頭的鴨子 繼承自超類 Duck那麼它也就擁有了超類中的行爲,實現超類的抽象方法,以展示自己不同於超類的特徵
 */
class MallardDuck extends Duck{

	@Override
	public void display() {
		System.out.println("我是綠頭鴨哦");
	}
	
}</span>

<span style="font-family:Courier New;font-size:14px;">/*
 *  外觀是紅頭的鴨子
 */
class ReadheadDuck extends Duck{

	@Override
	public void display() {
		System.out.println("我是紅頭鴨哦");
	}
	
}</span>

<span style="font-family:Courier New;font-size:14px;">public class DuckTest {
	public static void main(String[] args) {
		MallardDuck md = new MallardDuck();
		md.display();
		md.quack();
		md.swim();
	}
}</span>
這個程序已經寫好了,不過好像還沒有用到設計模式相關的東西,是的,讓我們一步一步的引入策略模式。

現在我們有一個需求,讓會飛的鴨子來超越其他的競爭者。如果讓我們去實現這樣的功能,可能會覺得很簡單啊,我們在超類Duck中,寫一個飛行類的方法,這樣他的子類不就都擁有了飛行行爲。恩,確實是這樣,但是有一個問題,並不是所有的鴨子都具有飛行行爲,比如在這個遊戲中會有一些木有鴨子,他們是不會飛甚至不會叫的。那麼剛纔的做法就會使得不會飛的鴨子也有了飛行的行爲,顯然是不合適的。

那麼我們是不是可以利用接口去達到剛纔的需求呢。我們把飛行的行爲以及呱呱叫的行爲從超類中抽取出來,放到Flyable和Quackable接口中,這樣就可以讓擁有這些行爲的類去實現這些接口.我們來看代碼:

<span style="font-family:Courier New;font-size:14px;">/*
 *  鴨子的抽象類
 */
public abstract class Duck {
	//鴨子外觀的抽象方法 
	public abstract void display();
	public void swim() {
		System.out.println("遊啊遊..");
	}
}

/*
 *  外觀是綠頭的鴨子
 */
class MallardDuck extends Duck implements Quackable,Flyable{

	@Override
	public void display() {
		System.out.println("我是綠頭鴨哦");
	}

	@Override
	public void fly() {
		System.out.println("我正在飛哦..");
	}

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

	
	
}
/*
 *  木頭鴨 不會飛也不會叫 外觀是木頭樣的顏色 假定因爲可以在水上漂 就相當於會游泳
 */
class WoodDuck extends Duck{

	@Override
	public void display() {
		System.out.println("我的外觀顏色是木般的顏色");
	}

}
/*
 * 飛行的接口
 */
interface Flyable {
	 void fly();
}
/*
 * 呱呱叫行爲的接口
 */
interface Quackable {
	 void quack();
}
public class DuckTest {
	public static void main(String[] args) {
		MallardDuck md = new MallardDuck();
		md.display();
		md.swim();
		md.quack();
		md.fly();
		WoodDuck wd = new WoodDuck();
		wd.swim();
		wd.display();
	}</span>
}

不錯,這樣是解決了剛纔的問題,可以有個問題,那麼就是代碼的複用性,假設我們有100個鴨子,都有同樣的飛行行爲,那麼這同樣的代碼我們要在每個子類中寫一遍,相當於100遍,即便粘貼複製,也要好久吧.如果要修改的話就更麻煩了。所以從代碼複用的角度來看,似乎又不太可行。設計中有一個原則就是:找出應用中可能需要變化的地方,將它們獨立出來,與那些不需要變化的代碼分離。

從上面的程序分析可知,需要變化的地方是飛行和呱呱叫的行爲,因爲不同的鴨子具有不同的飛行行爲或者是否會呱呱叫。所以根據設計原則,我們應該將他們抽取出來,建立一組新的類來代表每個行爲。這裏可能有人會有疑問,行爲怎麼可以成爲一個類呢?因爲飛行也會有屬性,比如飛行的速度,飛行的高度等。或者我們將這個行爲類想象成一個技能學習中心,父類中的行爲是從這裏學習得來的,子類也是。但如果我們要想在鴨子子類中可以動態的去改變飛行的行爲,該怎麼做?有這樣的一個設計原則:針對接口編程,而不是針對實現編程。所以我們用FlyBehavior和QuackBehavior接口來代表每個行爲,然後行爲的每個實現類都去實現其中的接口。這種做法跟以往不同,我們以往做法是行爲來自Duck超類的具體實現,或是繼承某個接口並由子去實現,這都是依賴於“實現”,這種做法不靈活。現在我們來看代碼實現:

<span style="font-family:Courier New;font-size:14px;">/*
 *  鴨子的抽象類
 */
public abstract class Duck {
	//行爲接口的變量
	QuackBehavior quackBehavior;
	FlyBehavior flyBehavior;
	//鴨子外觀的抽象方法 
	public abstract void display();
	
	public void performFly() {
		//委託給行爲類
		flyBehavior.fly();
	}
	public void performQuack() {
		quackBehavior.quack();
	}
	public void swim() {
		System.out.println("遊啊遊..");
	}
}
/*
 * 飛行的接口
 */
interface FlyBehavior{
	 public void fly();
}

//飛行的具體實現類
class FlyWithWing implements FlyBehavior {
	public void fly() {
		System.out.println("我會飛哦");
	};
}
class FlyNoWay implements FlyBehavior {

	@Override
	public void fly() {
		System.out.println("我不會飛啊");
	}
	
}
/*
 * 呱呱叫行爲的接口
 */
interface QuackBehavior {
	 public void quack();
}
class Quack implements QuackBehavior {
	@Override
	public void quack() {
		System.out.println("呱呱叫");
	}
}
class MuteQuack implements QuackBehavior {

	@Override
	public void quack() {
		System.out.println("我不會叫啊");
	}
	
}
/*
 *  外觀是綠頭的鴨子
 */
class MallardDuck extends Duck {
	public MallardDuck() {
		flyBehavior = new FlyWithWing();
		quackBehavior = new Quack();
	}
	@Override
	public void display() {
		System.out.println("我是綠頭鴨哦");
	}
	
}

/*
 *  木頭鴨 不會飛也不會叫 外觀是木頭樣的顏色 假定因爲可以在水上漂 就相當於會游泳
 */
class WoodDuck extends Duck{
	public WoodDuck() {
		flyBehavior = new FlyNoWay();
		quackBehavior = new MuteQuack();
	}
	@Override
	public void display() {
		System.out.println("我的外觀顏色是木般的顏色");
	}

}
public class DuckTest {
	public static void main(String[] args) {
		//向上轉型
		Duck md = new MallardDuck();
		md.display();
		md.performFly();
		md.performQuack();
		Duck wd = new WoodDuck();
		wd.display();
		wd.performFly();
		wd.performQuack();
	}
}</span>
雖然代碼量並沒有減少,但是可擴展性可維護性是增加的。現在我們雖然在main方法裏調用的是同一個方法,但是具體的結果卻不同。我們還可以在Duck類裏設定一個改變飛行行爲或其他行爲的方法,這樣子類就可以動態的去設定行爲了。

設計模式是一種思想,既然是思想上的東西,就需要我們不斷的思考,領悟,在實踐當中不斷的加以運用與驗證。雖然寫了這篇博客,但並不代表我已經徹底掌握,後面還需要不斷的理解、思考並將這些思想運用到實踐當中去。

ps:如要轉載,請註明出處。另推薦Head First 設計模式這本書,對於初學者來說是一本不錯的書,博客中的例子也是引用書裏面的。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             


發佈了46 篇原創文章 · 獲贊 63 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章