策略模式:
前言:
作者:韓數(shù)
Github:https://github.com/hanshuaikang
時間:2019-01-26
JDK版本:1.8
定義:
定義一系列的算法,把它們一個個封裝起來,并且使它們可相互替換沙峻。本模式使得算法可獨(dú)立于使用它的客戶而變化拟蜻。
適用范圍:
1.在一個系統(tǒng)中驯击,有很多相似的類产园,而區(qū)分這些類的僅僅是不同的行為。那么策略模式可以像電腦主機(jī)一樣模塊化的讓一個對象在不同的行為中選擇一種一種行為落君。
2护糖、一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種国觉。
3、如果一個對象有很多的行為又憨,如果不用恰當(dāng)?shù)哪J酱浠簦@些行為就只好使用多重的條件選擇語句來實現(xiàn)。
優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
代碼耦合度比較底蠢莺,相對來說比較靈活一些
可以避免使用if else多重判斷語句
比較有彈性壶运,可擴(kuò)展性比較好
缺點(diǎn)
策略類會比較多,之后的代碼實戰(zhàn)中會發(fā)現(xiàn)這個問題
所有策略類都需要對外暴露
前提引入:
韓數(shù)獨(dú)創(chuàng)之對話流:
老板: 阿呆浪秘,你去給我編寫一個鴨子類(嚴(yán)重吐槽蒋情,請大家不要想歪,本書依靠head frist系列書籍耸携,為了避免讀者讀書的時候代碼和書籍有不同的地方影響理解棵癣,故沒有修正)
阿呆: 內(nèi)心戲(不就寫個實體類嗎。寫個Duck類夺衍,然后把鴨子外貌狈谊,飛,叫這樣的特征定義了,方法實現(xiàn)了就OK了,Nice,完美)河劝,老板沒問題壁榕,保證完成任務(wù)!
a week has later.... 阿呆信心滿滿的把寫好的Duck類交給了老板赎瞎。
老板: 不錯牌里,寫的不錯,哎呀务甥,可是牡辽,我突然又想要一只橡皮鴨,這只鴨子敞临,不會飛态辛,吱吱叫,我小時候最喜歡的玩具挺尿,這樣吧奏黑,你去寫寫這個啥橡皮鴨吧。
阿呆:(內(nèi)心戲: 這橡皮鴨编矾,這熟史,跟我上次寫的那個鴨子不是一個品種啊,怎么還吱吱叫洽沟,鴨子不都嘎嘎嘎叫嗎以故,算了,不就是再寫一個類繼承Duck類嗎裆操,把fly怒详,quack,display這三個方法覆蓋重寫了就好了踪区,Nice昆烁,完美,我簡直是一個天才6懈凇)老板沒問題静尼,保證完成任務(wù)!
a week has later.... 阿呆信心滿滿的把寫好的RubberDuck類交給了老板传泊。
老板: 不錯鼠渺,真好,對了眷细,阿呆呀拦盹,我那個侄女,她喜歡那個綠毛鴨溪椎,會飛普舆,咕咕叫恬口,頭上長綠毛的那種,你看能寫么沼侣?
阿呆:(內(nèi)心戲:MMP祖能,略) 老板沒問題,保證完成任務(wù)蛾洛!
a week has later...
老板: 那個黑天鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒問題养铸,保證完成任務(wù)!
老板: 那個七小天鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒問題雅潭,保證完成任務(wù)揭厚!
老板: 那個派大鴨...
阿呆:(內(nèi)心戲:emmmmmp) 老板沒問題却特,保證完成任務(wù)扶供!
a year has later...
阿呆:卒
這么玩兒下去肯定不行,只通過繼承裂明,必然可以完成老板的要求椿浓,萬一有一萬只不同品種的鴨子,不敢往下想了闽晦,而且Duck是所有類的父類扳碍,這要是Duck改一點(diǎn)點(diǎn),想到后面還有幾萬個Duck的孩子要改仙蛉,不禁倒吸一口涼氣笋敞,這是,阿呆的弟弟二呆出場了荠瘪,說:
這世間鴨子千千萬夯巷,不過數(shù)種,記得我之前給你講那個電腦主機(jī)的故事么哀墓,把所有零件設(shè)計成可拆卸更換的模塊
只留下那個大家通用的模塊不要動趁餐,在鴨子身上就是游泳,哪種鴨子不會游泳篮绰?你說后雷,其他的,飛呀吠各,叫什么的臀突,我們單獨(dú)分離出來,最后老板要啥鴨子贾漏,我們給他組裝一下不就得了候学。
此時,設(shè)計模式中一句寶典浮出水面磕瓷,那就是:分離變和不變的部分盒齿。
大家聽了不禁嘖嘖稱贊念逞,紛紛嘆道妙呀,妙呀边翁,真是妙棒岢小!
代碼實戰(zhàn):
煥然一新后的鴨子類:
public abstract class Duck {
?
/*
* 面向超類編程符匾,主類Duck只保留所有鴨子通用不變的特征比如游泳
* 變化的部分單獨(dú)封裝叨咖,提高代碼的彈性,避免因為單一的向下繼承
* 造成的代碼的靈活性降低啊胶,避免過于耦合情況的發(fā)生甸各。
*/
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
//定義set方法,可以動態(tài)的設(shè)定鴨子飛行的行為
public void setFlyBehavior (FlyBehavior fb) {
flyBehavior = fb;
}
//定義set方法焰坪,可以動態(tài)的設(shè)定鴨子飛行的行為
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
//鴨子的外表趣倾,這里定義為抽象方法,父類只做聲明某饰,不負(fù)責(zé)實現(xiàn)
abstract void display();
//鴨子的行為儒恋,Duck不適合實現(xiàn),交給相應(yīng)的模塊實現(xiàn)黔漂。
public void performFly() {
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
//所有鴨子都會游泳
public void swim() {
System.out.println("All ducks float, even decoys!");
}
}
可能會有人不太懂诫尽,F(xiàn)lyBehavior flyBehavior; QuackBehavior quackBehavior;是什么意思,你想啊炬守,雖然顯卡,CPU牧嫉,音響,內(nèi)存條都模塊化了减途,但是也總的留個插頭方便接入不是酣藻。
定義飛行行為的接口,為啥是接口呢观蜗?不是類臊恋,面向接口(超類)編程,可以更好的利用面向?qū)ο笾械亩鄳B(tài)墓捻,第二個也可以提高程序相互調(diào)用中的安全性抖仅。提高程序的靈活性,可擴(kuò)展性砖第。
public interface FlyBehavior {
public void fly();
}
同理叫聲接口:
public interface QuackBehavior {
public void quack();
}
比如嘎嘎叫的鴨子撤卢,我們就定義一個Quack類實現(xiàn)QuackBehavior的接口,并編寫quack方法的實現(xiàn)為嘎嘎叫梧兼,吱吱叫的鴨子放吩,我們就定義一個Squeak類實現(xiàn)QuackBehavior的接口,并編寫quack方法的實現(xiàn)為吱吱叫羽杰,等等渡紫,咕咕叫到推,喔喔叫,哇我叫惕澎,等等等等等莉测,你開心就好。飛的行為同理唧喉。
/***
*
* 定義鴨子叫聲是嘎嘎嘎的行為
*
*/
?
?
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("嘎嘎嘎");
}
}
?
?
?
/***
*
* @author hansu
* 定義鴨子吱吱叫的行為
*
*/
?
?
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("吱吱吱");
}
}
?
/***
*
* 定義鴨子不會飛的行為
*
*
*/
?
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("哎捣卤,難過,我不會飛");
}
}
?
?
?
/***
*
* 定義鴨子是會飛的行為
*
*/
?
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("我會飛!哈哈哈");
}
}
?
好了八孝,現(xiàn)在模塊是寫好了董朝,可是,我們怎么樣編寫鴨子的子類把這些模塊裝上去呢干跛?二呆緩緩說子姜,急啥,且聽我娓娓道來驯鳖。
喲闲询,要是想把這些模塊來組裝
那你就要深入進(jìn)去它的心房
把模塊放入構(gòu)造器中
變成一把直接就上膛的手槍
yo久免,freestyle
比如橡皮鴨的特征是吱吱叫浅辙,不會飛,身子是橡皮做的阎姥,于是就把FlyNoWay记舆,Squeak模塊組裝一下,然后只需實現(xiàn)一下父類Duck的display方法就會得到一只嶄新的完全滿足甲方要求的橡皮鴨了呼巴!
如下:
/***
*
* demo1:橡皮鴨泽腮,特征,不會飛衣赶,吱吱叫
*
*/
?
public class RubberDuck extends Duck {
public RubberDuck() {
/*
* 注:因為RubberDuck繼承Duck類诊赊,所有Duck類中定義的
* flyBehavior和quackBehavior可以直接賦值
*/
//定義橡皮鴨不會飛的行為
flyBehavior = new FlyNoWay();
//定義橡皮鴨吱吱叫的行為
quackBehavior = new Squeak();
}
public void display() {
System.out.println("我是一個橡皮鴨,我的身體是橡皮做噠");
}
}
編寫測試代碼Text:
public class Text {
public static void main(String[] args) {
Text t = new Text();
t.rubberDuckDemoText();
System.out.println("\n現(xiàn)在有請活的鴨子閃亮登場府瞄!\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:
我是一個橡皮鴨碧磅,我的身體是橡皮做噠 I can't fly 吱吱吱
現(xiàn)在有請活的鴨子閃亮登場!
我是一只活鴨子 我會飛!哈哈哈 嘎嘎嘎
最后遵馆,二呆和老板幸福的生活在了一起鲸郊。
實戰(zhàn)總結(jié):
在這里大家就會發(fā)現(xiàn)了,雖然這樣的確比繼承單一Duck類重寫方法方便高效了很多货邓,但是如果鴨子特征超級多的話秆撮,也需要編寫超級多的行為類,同時换况,每個行為類都必須是可實例化的职辨,這針對某些情況來說并不太適合盗蟆,但是,設(shè)計模式有二十七種呢舒裤,更不要說其他設(shè)計模式了姆涩,更是多到數(shù)不勝數(shù),所以在合適的情況下選擇合適的設(shè)計模式可以顯著提高我們代碼的效率和質(zhì)量惭每,這點(diǎn)是毋庸置疑的骨饿。
寫在最后:
歡迎大家給小星星,您的星星是我寫下去的不竭動力台腥!
源碼部分請移步本人Github下載:
Github地址:
Github:https://github.com/hanshuaikang/design-pattern-java
參考資料:
菜鳥教程:http://www.runoob.com/design-pattern/strategy-pattern.html
Head frist of 設(shè)計模式