策略模式,是我們接觸到的第一個(gè)設(shè)計(jì)模式,也是較容易理解的一個(gè)模式栅盲。
我們可以給它下一個(gè)定義:
** 定義了算法族,分別封裝起來废恋,讓它們之間可以互相轉(zhuǎn)換谈秫,此模式讓算法的獨(dú)立于使用算法的客戶。**
維基百科上的定義是:** a software design pattern that enables an algorithm's behavior to be selected at runtime. **
維基百科上的強(qiáng)調(diào)了算法行為是在運(yùn)行時(shí)決定的鱼鼓,這正是策略模式很關(guān)鍵的一點(diǎn)拟烫。
引子
假設(shè)我們現(xiàn)在要設(shè)計(jì)一個(gè)鴨子類Duck類,然后讓不同的鴨子繼承于它迄本。我們把目光聚焦到鴨子的行為上硕淑。如果我們要給鴨子增加一個(gè)行為“fly”,第一個(gè)想法,在抽象類duck里添加一個(gè)fly方法就可置媳,其余鴨子繼承實(shí)現(xiàn)這個(gè)方法于樟。
但是這就出現(xiàn)了一個(gè)問題,并不是所有鴨子都會(huì)飛拇囊,我們反而讓一些本不具備這個(gè)fly行為的鴨子也具有該行為迂曲。那怎么辦呢?
利用繼承來提供鴨子的行為寥袭,會(huì)導(dǎo)致下面這些后果:
- 代碼在多個(gè)子類中重復(fù)路捧,如果兩類不同鴨子需要同一種fly行為,我們就要在兩個(gè)類里分別覆蓋兩次纠永,這樣萬一維護(hù)起來是非常困難的
- 很難知道所有鴨子的全部行為
- 運(yùn)行時(shí)的行為不容易改變
- 改變會(huì)一發(fā)動(dòng)全身鬓长,造成其他鴨子不想要改變
設(shè)計(jì)原則1
軟件開發(fā)中,我們常常需要遵守的設(shè)計(jì)原則是:
** 把可能需要變化的地方獨(dú)立出來尝江,不要和那些不需要變化的代碼混在一起 **
這樣代碼變化引起的不經(jīng)意后果變少涉波,系統(tǒng)變得更有彈性
實(shí)際就是盡量讓系統(tǒng)中某部分的改變不影響其他部分的變化。
提取鴨子的的行為
根據(jù)設(shè)計(jì)原則炭序,鴨子飛行的行為會(huì)發(fā)生變化啤覆,所以我們需要將fly行為單獨(dú)提取出來。同理惭聂,我們提取出兩個(gè)鴨子可能變化的行為fly和quack鴨叫窗声。用兩組類分別代表fly和quack行為。
設(shè)計(jì)原則2
那么我們?nèi)绾文莾山M鴨子行為的類呢辜纲?這里引出第二個(gè)我們提出的設(shè)計(jì)原則:
** 面對(duì)接口編程笨觅,而不是面對(duì)實(shí)現(xiàn)編程 **
這樣就可以實(shí)現(xiàn)在運(yùn)行時(shí)改變鴨子的行為。
我們不會(huì)直接指定特定的行為給鴨子耕腾。而是聲明兩個(gè)接口FlyBehavior和QuackBehavior见剩。我們制造的其他一系列的類專門來實(shí)現(xiàn)FlyBehavior和QuackBehavior,這組就成為行為類扫俺,或者算法類苍苞。
用行為類來實(shí)現(xiàn)接口而不是利用duck類來實(shí)現(xiàn)。
實(shí)現(xiàn)鴨子的行為
根據(jù)設(shè)計(jì)原則2狼纬,可以讓飛行和鴨叫行為的動(dòng)作被其他對(duì)象復(fù)用羹呵,因?yàn)檫@些為行為已經(jīng)與鴨子類無關(guān)了。
而且當(dāng)我們新增一些行為的時(shí)候疗琉,不會(huì)影響到既有的行為類冈欢,也不會(huì)影響鴨子類。太棒了盈简!
** 很多同學(xué)都覺得這里用類來代表行為是不是覺得很奇怪凑耻。在大家默認(rèn)里犯戏,類應(yīng)該是代表某種東西的,類應(yīng)該擁有狀態(tài)與行為拳话。這里,我們需要糾正這個(gè)觀點(diǎn)种吸,一個(gè)行為也可以具有各種屬性和函數(shù)弃衍。類不僅僅是用來代表東西事物的。 **
整合實(shí)現(xiàn)我們?cè)O(shè)計(jì)的鴨子類
首先坚俗,在duck類中加入兩個(gè)實(shí)例變量镜盯,分別聲明為兩個(gè)接口的類型,每個(gè)鴨子對(duì)象都會(huì)動(dòng)態(tài)的設(shè)置這些變量以便在運(yùn)行時(shí)引用正確的行為類型
duck類的實(shí)現(xiàn)
package strategyPattern;
//抽象的Duck類猖败,所有鴨子都繼承
public abstract class Duck {
//為行為接口類型聲明兩個(gè)引用變量速缆,所有鴨子類都繼承它們
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public Duck() {
}
public abstract void display();
public void performFly() {
flyBehavior.fly(); //委托給fly行為類實(shí)現(xiàn)
}
public void performQuack() {
quackBehavior.quack(); //委托給quack行為類實(shí)現(xiàn)
}
//swim是所有鴨子都共同擁有的方法,所以可以直接在在duck類中實(shí)現(xiàn)
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
FlyBehavior接口的實(shí)現(xiàn):
package strategyPattern;
//所有飛行行為類必須實(shí)現(xiàn)的接口
public interface FlyBehavior {
public void fly();
}
兩個(gè)fly行為實(shí)現(xiàn)類恩闻,繼承至FlyBehavior接口
package strategyPattern;
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I'm flying with wings!");
}
}
package strategyPattern;
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can't fly!");
}
}
Quack接口及其三個(gè)行為實(shí)現(xiàn)類:
package strategyPattern;
public class Quack implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("Quack");
}
}
package strategyPattern;
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("<<silence>>");
}
}
package strategyPattern;
public class Squeak implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("Squeak");
}
}
package strategyPattern;
public interface QuackBehavior {
public void quack();
}
一個(gè)MallardDuck類繼承至Duck類:
package strategyPattern;
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("I'm a real Mallard duck艺糜!");
}
}
測(cè)試類
package strategyPattern;
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
//這里調(diào)用mallardduck繼承來的performFly方法,進(jìn)而委托給該對(duì)象的quackBehavior對(duì)象處理
//也就是最后是調(diào)用了繼承來的quackBehavior引用對(duì)象的quack()方法
mallard.performFly();
mallard.performQuack();
}
}
運(yùn)行結(jié)果:
在這里為了實(shí)現(xiàn)動(dòng)態(tài)的改變鴨子的行為幢尚,我們可以新建一個(gè)flyrocketPowered行為類破停,然后動(dòng)態(tài)的改變其行為:
package strategyPattern;
public class FlyRocketPowered implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I'm flying with a rocket ");
}
}
package strategyPattern;
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
//這里調(diào)用mallardduck繼承來的performFly方法,進(jìn)而委托給該對(duì)象的quackBehavior對(duì)象處理
//也就是最后是調(diào)用了繼承來的quackBehavior引用對(duì)象的quack()方法
mallard.performFly();
mallard.performQuack();
Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
運(yùn)行結(jié)果:
每一個(gè)鴨子都有一個(gè)FlyBehavior和一個(gè)quackBehavior尉剩,好將飛行和鴨叫委托給他們代為處理真慢。
當(dāng)你將兩個(gè)類結(jié)合起來使用時(shí),如同本例理茎,這就是組合composition黑界。這種做法和繼承不同的地方在于,鴨子的行為不是繼承來的而是和適當(dāng)行為對(duì)象那個(gè)組合來的皂林。
設(shè)計(jì)原則3:
** 多用組合 少用繼承 **
策略模式總結(jié)
三個(gè)設(shè)計(jì)原則:
- 封裝變化朗鸠,分開變化與不變
- 多用組合,少用繼承
- 面向接口編程式撼,而不是面對(duì)實(shí)現(xiàn)編程
策略模式:
** 定義了算法族童社,分別封裝起來,讓它們之間可以互相轉(zhuǎn)換著隆,此模式讓算法的獨(dú)立于使用算法的客戶扰楼。**
實(shí)現(xiàn)策略模式,我們需要對(duì)行為或算法實(shí)現(xiàn)各自的接口美浦,具體的實(shí)現(xiàn)交給繼承自這些接口的行為類弦赖,不需要在我們的主類鴨子中實(shí)現(xiàn)。主類鴨子聲明兩個(gè)接口的引用的實(shí)例變量浦辨,并設(shè)計(jì)set方法蹬竖,這樣就能在運(yùn)行時(shí)動(dòng)態(tài)的改變行為。實(shí)現(xiàn)獨(dú)立和復(fù)用。