策略模式:
前言:
作者:韓數(shù)
Github:https://github.com/hanshuaikang
時(shí)間:2019-01-26
JDK版本:1.8
定義:
定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái),并且使它們可相互替換。本模式使得算法可獨(dú)立于使用它的客戶而變化。
適用范圍:
1.在一個(gè)系統(tǒng)中,有很多相似的類,而區(qū)分這些類的僅僅是不同的行為。那么策略模式可以像電腦主機(jī)一樣模塊化的讓一個(gè)對(duì)象在不同的行為中選擇一種一種行為。
2、一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種。
3、如果一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重的條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)。
優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
代碼耦合度比較底,相對(duì)來(lái)說(shuō)比較靈活一些
可以避免使用if else多重判斷語(yǔ)句
比較有彈性,可擴(kuò)展性比較好
缺點(diǎn)
策略類會(huì)比較多,之后的代碼實(shí)戰(zhàn)中會(huì)發(fā)現(xiàn)這個(gè)問(wèn)題
所有策略類都需要對(duì)外暴露
前提引入:
韓數(shù)獨(dú)創(chuàng)之對(duì)話流:
老板: 阿呆,你去給我編寫一個(gè)鴨子類(嚴(yán)重吐槽,請(qǐng)大家不要想歪,本書依靠head frist系列書籍,為了避免讀者讀書的時(shí)候代碼和書籍有不同的地方影響理解,故沒(méi)有修正)
阿呆: 內(nèi)心戲(不就寫個(gè)實(shí)體類嗎。寫個(gè)Duck類,然后把鴨子外貌,飛,叫這樣的特征定義了,方法實(shí)現(xiàn)了就OK了,Nice,完美),老板沒(méi)問(wèn)題,保證完成任務(wù)!
a week has later.... 阿呆信心滿滿的把寫好的Duck類交給了老板。
老板: 不錯(cuò),寫的不錯(cuò),哎呀,可是,我突然又想要一只橡皮鴨,這只鴨子,不會(huì)飛,吱吱叫,我小時(shí)候最喜歡的玩具,這樣吧,你去寫寫這個(gè)啥橡皮鴨吧。
阿呆:(內(nèi)心戲: 這橡皮鴨,這,跟我上次寫的那個(gè)鴨子不是一個(gè)品種啊,怎么還吱吱叫,鴨子不都嘎嘎嘎叫嗎,算了,不就是再寫一個(gè)類繼承Duck類嗎,把fly,quack,display這三個(gè)方法覆蓋重寫了就好了,Nice,完美,我簡(jiǎn)直是一個(gè)天才?。├习鍥](méi)問(wèn)題,保證完成任務(wù)!
a week has later.... 阿呆信心滿滿的把寫好的RubberDuck類交給了老板。
老板: 不錯(cuò),真好,對(duì)了,阿呆呀,我那個(gè)侄女,她喜歡那個(gè)綠毛鴨,會(huì)飛,咕咕叫,頭上長(zhǎng)綠毛的那種,你看能寫么?
阿呆:(內(nèi)心戲:MMP,略) 老板沒(méi)問(wèn)題,保證完成任務(wù)!
a week has later...
老板: 那個(gè)黑天鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒(méi)問(wèn)題,保證完成任務(wù)!
老板: 那個(gè)七小天鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒(méi)問(wèn)題,保證完成任務(wù)!
老板: 那個(gè)派大鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒(méi)問(wèn)題,保證完成任務(wù)!
a year has later...
阿呆:卒
這么玩兒下去肯定不行,只通過(guò)繼承,必然可以完成老板的要求,萬(wàn)一有一萬(wàn)只不同品種的鴨子,不敢往下想了,而且Duck是所有類的父類,這要是Duck改一點(diǎn)點(diǎn),想到后面還有幾萬(wàn)個(gè)Duck的孩子要改,不禁倒吸一口涼氣,這是,阿呆的弟弟二呆出場(chǎng)了,說(shuō):
這世間鴨子千千萬(wàn),不過(guò)數(shù)種,記得我之前給你講那個(gè)電腦主機(jī)的故事么,把所有零件設(shè)計(jì)成可拆卸更換的模塊
只留下那個(gè)大家通用的模塊不要?jiǎng)?,在鴨子身上就是游泳,哪種鴨子不會(huì)游泳?你說(shuō),其他的,飛呀,叫什么的,我們單獨(dú)分離出來(lái),最后老板要啥鴨子,我們給他組裝一下不就得了。
此時(shí),設(shè)計(jì)模式中一句寶典浮出水面,那就是:分離變和不變的部分。
大家聽(tīng)了不禁嘖嘖稱贊,紛紛嘆道妙呀,妙呀,真是妙?。?/p>
代碼實(shí)戰(zhàn):
煥然一新后的鴨子類:
public abstract class Duck {
?
/*
* 面向超類編程,主類Duck只保留所有鴨子通用不變的特征比如游泳
* 變化的部分單獨(dú)封裝,提高代碼的彈性,避免因?yàn)閱我坏南蛳吕^承
* 造成的代碼的靈活性降低,避免過(guò)于耦合情況的發(fā)生。
*/
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
//定義set方法,可以動(dòng)態(tài)的設(shè)定鴨子飛行的行為
public void setFlyBehavior (FlyBehavior fb) {
flyBehavior = fb;
}
//定義set方法,可以動(dòng)態(tài)的設(shè)定鴨子飛行的行為
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
//鴨子的外表,這里定義為抽象方法,父類只做聲明,不負(fù)責(zé)實(shí)現(xiàn)
abstract void display();
//鴨子的行為,Duck不適合實(shí)現(xiàn),交給相應(yīng)的模塊實(shí)現(xiàn)。
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
//所有鴨子都會(huì)游泳
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
可能會(huì)有人不太懂,F(xiàn)lyBehavior flyBehavior; QuackBehavior quackBehavior;是什么意思,你想啊,雖然顯卡,CPU,音響,內(nèi)存條都模塊化了,但是也總的留個(gè)插頭方便接入不是。
定義飛行行為的接口,為啥是接口呢?不是類,面向接口(超類)編程,可以更好的利用面向?qū)ο笾械亩鄳B(tài),第二個(gè)也可以提高程序相互調(diào)用中的安全性。提高程序的靈活性,可擴(kuò)展性。
public interface FlyBehavior {
public void fly();
}
同理叫聲接口:
public interface QuackBehavior {
public void quack();
}
比如嘎嘎叫的鴨子,我們就定義一個(gè)Quack類實(shí)現(xiàn)QuackBehavior的接口,并編寫quack方法的實(shí)現(xiàn)為嘎嘎叫,吱吱叫的鴨子,我們就定義一個(gè)Squeak類實(shí)現(xiàn)QuackBehavior的接口,并編寫quack方法的實(shí)現(xiàn)為吱吱叫,等等,咕咕叫,喔喔叫,哇我叫,等等等等等,你開(kāi)心就好。飛的行為同理。
/***
*
* 定義鴨子叫聲是嘎嘎嘎的行為
*
*/
?
?
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("嘎嘎嘎");
}
}
?
?
?
/***
*
* @author hansu
* 定義鴨子吱吱叫的行為
*
*/
?
?
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("吱吱吱");
}
}
?
/***
*
* 定義鴨子不會(huì)飛的行為
*
*
*/
?
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("哎,難過(guò),我不會(huì)飛");
}
}
?
?
?
/***
*
* 定義鴨子是會(huì)飛的行為
*
*/
?
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("我會(huì)飛!哈哈哈");
}
}
?
好了,現(xiàn)在模塊是寫好了,可是,我們?cè)趺礃泳帉戻喿拥淖宇惏堰@些模塊裝上去呢?二呆緩緩說(shuō),急啥,且聽(tīng)我娓娓道來(lái)。
喲,要是想把這些模塊來(lái)組裝
那你就要深入進(jìn)去它的心房
把模塊放入構(gòu)造器中
變成一把直接就上膛的手槍
yo,freestyle
比如橡皮鴨的特征是吱吱叫,不會(huì)飛,身子是橡皮做的,于是就把FlyNoWay,Squeak模塊組裝一下,然后只需實(shí)現(xiàn)一下父類Duck的display方法就會(huì)得到一只嶄新的完全滿足甲方要求的橡皮鴨了!
如下:
/***
*
* demo1:橡皮鴨,特征,不會(huì)飛,吱吱叫
*
*/
?
public class RubberDuck extends Duck {
public RubberDuck() {
/*
* 注:因?yàn)镽ubberDuck繼承Duck類,所有Duck類中定義的
* flyBehavior和quackBehavior可以直接賦值
*/
//定義橡皮鴨不會(huì)飛的行為
flyBehavior = new FlyNoWay();
//定義橡皮鴨吱吱叫的行為
quackBehavior = new Squeak();
}
public void display() {
System.out.println("我是一個(gè)橡皮鴨,我的身體是橡皮做噠");
}
}
編寫測(cè)試代碼Text:
public class Text {
public static void main(String[] args) {
Text t = new Text();
t.rubberDuckDemoText();
System.out.println("\n現(xiàn)在有請(qǐng)活的鴨子閃亮登場(chǎng)!\n");
t.liveDuckDemoText();
}
public void rubberDuckDemoText() {
RubberDuck rubberDuck = new RubberDuck();
rubberDuck.display();
rubberDuck.performFly();
rubberDuck.performQuack();
?
}
public void liveDuckDemoText() {
LiveDuck liveDuck = new LiveDuck();
liveDuck.display();
liveDuck.performFly();
liveDuck.performQuack();
}
?
}
Out:
我是一個(gè)橡皮鴨,我的身體是橡皮做噠 I can't fly 吱吱吱
現(xiàn)在有請(qǐng)活的鴨子閃亮登場(chǎng)!
我是一只活鴨子 我會(huì)飛!哈哈哈 嘎嘎嘎
最后,二呆和老板幸福的生活在了一起。
實(shí)戰(zhàn)總結(jié):
在這里大家就會(huì)發(fā)現(xiàn)了,雖然這樣的確比繼承單一Duck類重寫方法方便高效了很多,但是如果鴨子特征超級(jí)多的話,也需要編寫超級(jí)多的行為類,同時(shí),每個(gè)行為類都必須是可實(shí)例化的,這針對(duì)某些情況來(lái)說(shuō)并不太適合,但是,設(shè)計(jì)模式有二十七種呢,更不要說(shuō)其他設(shè)計(jì)模式了,更是多到數(shù)不勝數(shù),所以在合適的情況下選擇合適的設(shè)計(jì)模式可以顯著提高我們代碼的效率和質(zhì)量,這點(diǎn)是毋庸置疑的。
寫在最后:
歡迎大家給小星星,您的星星是我寫下去的不竭動(dòng)力!
源碼部分請(qǐng)移步本人Github下載:
Github地址:
Github:https://github.com/hanshuaikang/design-pattern-java
參考資料:
菜鳥(niǎo)教程:http://www.runoob.com/design-pattern/strategy-pattern.html
Head frist of 設(shè)計(jì)模式