日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

设计模式(一)————策略模式(张三的故事??)

發(fā)布時(shí)間:2025/3/21 asp.net 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设计模式(一)————策略模式(张三的故事??) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

引言

當(dāng)我們完成一個(gè)復(fù)雜的業(yè)務(wù)中常常會面臨一個(gè)問題:針對一個(gè)對象的某個(gè)行為,不同的情境下有不同的處理方式;
就比如今天我要去上班,那么我需要以哪種交通方式去上班呢?可以有下面幾種選擇:

  • 步行
  • 公交
  • 地鐵
  • 自行車
  • 開車

當(dāng)然還會有更多的選擇,這只是列舉了幾種;我上班時(shí)會在不同的情況下選擇不同的交通工具,這就是不同處理方式;
如果在代碼中體現(xiàn),我們可以選擇用 if…else 或者 switch 來對不同的情境進(jìn)行判斷,從而選擇相應(yīng)的交通工具;這樣想當(dāng)然很簡單,但是寫出來的代碼會很復(fù)雜,而且后期進(jìn)行維護(hù)是很難的;
那么就需要想一個(gè)辦法將這個(gè)對象和這個(gè)行為分開,這個(gè)行為就是一個(gè)算法,這樣對于修改維護(hù)只需要針對這個(gè)算法就可以了;

這里就需要用到設(shè)計(jì)模式中的策略模式;

策略模式定義:
策略模式(Strategy Pattern)中,定義算法族(策略組),分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶;

下面我將會用一個(gè)模擬鴨子的例子來介紹這個(gè)模式;

張三的問題

某公司有一個(gè)模擬鴨子的業(yè)務(wù),這個(gè)業(yè)務(wù)中將會定義多種不同類型的鴨子,有的會飛,有的會叫,并且飛行方式和叫聲可能也會有不同;現(xiàn)在這個(gè)業(yè)務(wù)交給了張三去做,張三以熟練的OO技術(shù),設(shè)計(jì)了一個(gè)父類為Duck,然后讓各種不同的鴨子去繼承這個(gè)父類,便輕而易舉的完成了這個(gè)項(xiàng)目;

類圖:

可以看到,紅頭鴨和綠頭鴨都繼承了Dunk并重寫了display方法,是不是很簡單,我們看看代碼:

Duck類:

// 鴨子類(抽象類) public abstract class Duck {// 抽象方法:顯示什么鴨子public abstract void display();// 飛行的方法public void fly() {System.out.println("I'm flying!!!");}// 叫的方法public void quack() {System.out.println("嘎嘎嘎");} }

MallardDuck類

// 綠頭鴨 public class MallardDuck extends Duck{@Overridepublic void display() {System.out.println("我是一只綠頭鴨!!");} }

RedheadDuck類

// 紅頭鴨 public class RedheadDuck extends Duck{@Overridepublic void display() {System.out.println("我是一只紅頭鴨!!");} }

輸出測試一下:

// 測試相應(yīng)的功能 public class MiniDuckSimulator {public static void main(String[] args) {Duck mallardDuck = new MallardDuck(); // 綠頭鴨mallardDuck.display();mallardDuck.fly();mallardDuck.quack();System.out.println("-----------------");Duck redheadDuck = new RedheadDuck(); // 紅頭鴨redheadDuck.display();redheadDuck.fly();redheadDuck.quack();System.out.println("-----------------");} }

結(jié)果如下:

我是一只綠頭鴨!! I'm flying!!! 嘎嘎嘎 ----------------- 我是一只紅頭鴨!! I'm flying!!! 嘎嘎嘎 -----------------

就在張三沾沾自喜的時(shí)候,這時(shí)產(chǎn)品經(jīng)理提出了新的要求:要加一種新的鴨子:橡皮鴨;
張三一想:橡皮鴨不能飛,而且橡皮鴨的叫聲是“吱吱吱”,那么只要?jiǎng)?chuàng)建一個(gè)橡皮鴨的類,依然繼承Dunk,然后重寫Dunk中的fly和quack方法不久可以了嘛;
于是他又加了一個(gè)類:

RubberDuck類

// 橡皮鴨不會飛,叫聲是吱吱吱,所以需要重寫所有方法 public class RubberDuck extends Duck{@Overridepublic void display() {System.out.println("我是一只橡皮鴨!!");}@Overridepublic void fly() {System.out.println("I can't fly!!!");}@Overridepublic void quack() {System.out.println("吱吱吱");} }

這時(shí)張三突然意識到,這里使用繼承好像并不是特別完美,雖然實(shí)現(xiàn)了功能,但是RubberDuck類把父類中的所有方法都重寫了一遍,這樣的代碼就出現(xiàn)了重復(fù)啊;

張三又想:
Duck類中的fly和quack好像并不適用于所有鴨子的情況,有的鴨子不會飛,有的不會叫,有的叫聲不一樣,有的飛行方式不一樣。。。。這樣看來,這個(gè)父類并不是完美的;如果產(chǎn)品經(jīng)理讓我給鴨子多加一個(gè)游泳的行為,那么一旦加到Duck類中后,所有種類的鴨子可能都會面臨修改的可能,這樣也太麻煩了吧!那該怎么辦呢?

這里總結(jié)一下張三通過繼承來提供Duck行為存在的問題:

  • 代碼在多個(gè)子類中重復(fù)
  • 運(yùn)行時(shí)的行為不易改變
  • 很難知道不同的鴨子具體的行為
  • 改變會牽一發(fā)動(dòng)全身,造成其他種類鴨子不需要的改變;

該怎么做?

這時(shí)張三突然想到:可以使用一個(gè)Flyable和一個(gè)Quackable接口,只讓會飛的鴨子實(shí)現(xiàn)Flyable,會叫的鴨子實(shí)現(xiàn)Quackable接口不就可以了嘛;

真的可以嗎?
這樣的話就會重復(fù)的代碼會更多,如果需要修改飛行的行為,那么就需要對所有實(shí)現(xiàn)飛行接口的代碼進(jìn)行修改;一旦需要加入新的行為,如果用接口,那就需要對所有的鴨子進(jìn)行一個(gè)判斷并實(shí)現(xiàn)該行為;
因?yàn)樵谶@里張三只聲明了三種類型的鴨子,如果是五十種呢?一百種呢?難道都需要一一修改嗎?

其實(shí)出現(xiàn)這些問題的本質(zhì)就是因?yàn)轼喿覦uck的行為會在子類里不斷地改變,并且如果讓所有的子類都有這些行為也是不現(xiàn)實(shí)的;

且使用接口不能實(shí)現(xiàn)代碼,就無法達(dá)到代碼的復(fù)用,一旦修改了某個(gè)行為,就需要找到所有實(shí)現(xiàn)該行為的類去修改,不僅工作量更大,而且可能會出現(xiàn)新的錯(cuò)誤;

這時(shí)李四給張三提建議:
只需要找到項(xiàng)目中可能需要變換的地方,并把這個(gè)變化獨(dú)立出來,不和那些不會變化的代碼混在一起不就可以了嗎?

李四的意思其實(shí)就是:把Dunk中會變化的部分取出來,單獨(dú)封裝起來,這樣就可以輕易實(shí)現(xiàn)更該和擴(kuò)充該部分,且不會影響不會變化的內(nèi)容;

那么下面張三需要做的就是:將鴨子變化的行為從Duck中取出封裝起來了;

問題解決

張三對Duck進(jìn)行一個(gè)分析:既然要分離變化的行為,那么在這個(gè)類中也就只有fly和quack行為會改變了,所以只需要把這倆行為拿出來然后封裝起來就可以了;

這時(shí)又有了一個(gè)新的問題:
張三希望代碼更有彈性,因?yàn)殚_始的代碼沒有彈性才這樣做的,如果能夠動(dòng)態(tài)的改變鴨子的行為,那樣一旦有需求改變肯定會容易很多;

張三靈機(jī)一動(dòng),想到了一個(gè)設(shè)計(jì)原則:
面向接口編程,而不是針對實(shí)現(xiàn)編程;

那么就是用接口來抽象這個(gè)行為,具體行為的表現(xiàn)模式實(shí)現(xiàn)這個(gè)接口就可以了;
所以張三準(zhǔn)備了一個(gè)QuackBehavior接口和一個(gè)FlyBehavior接口,然后將他們聚合到Duck類中,這樣就可以靈活的修改代碼了;

由于產(chǎn)品經(jīng)理提出了新的需求:增加一個(gè)不會飛不會叫的模型鴨,并且給它加一個(gè)火箭助力器,讓它可以飛;
張三想:正好我在重新設(shè)計(jì)代碼,不如就拿這個(gè)來試試代碼,看看能不能達(dá)到預(yù)期要求;

張三先設(shè)計(jì)了QuackBehavior接口和FlyBehavior接口的類圖

那么Dunk該怎么設(shè)計(jì)呢?我們可以讓Dunk關(guān)聯(lián)于這兩個(gè)接口,這樣就可以讓Dunk類使用對應(yīng)的方法了;
類圖:

張三這次留了個(gè)心眼,為了能夠?qū)崿F(xiàn)運(yùn)行時(shí)代碼的動(dòng)態(tài)拓展,所以加入了set方法,這樣就可以隨時(shí)隨地的設(shè)置不同鴨子的行為了;
接下來就是實(shí)現(xiàn)代碼了;

FlyBehavior接口

// 鴨子飛的接口 public interface FlyBehavior {public void fly(); }

實(shí)現(xiàn)FlyBehavior接口:

// 用翅膀飛 public class FlyWithWings implements FlyBehavior{@Overridepublic void fly() {System.out.println("I'm flying!!!");} } // 不能飛 public class FlyNoWay implements FlyBehavior{@Overridepublic void fly() {System.out.println("I can't flying");} } // 火箭噴射器飛 public class FlyWithRocket implements FlyBehavior {@Overridepublic void fly() {System.out.println("Fly with a rocket!!");} }

QuackBehavior接口

// 鴨子叫的接口 public interface QuackBehavior {public void quack(); }

實(shí)現(xiàn)QuackBehavior接口

// 鴨子嘎嘎叫 public class Quack implements QuackBehavior{@Overridepublic void quack() {System.out.println("嘎嘎嘎");} } // 橡皮鴨吱吱叫 public class Squeak implements QuackBehavior{@Overridepublic void quack() {System.out.println("吱吱吱");} } // 不會叫 public class MuteQuack implements QuackBehavior{@Overridepublic void quack() {System.out.println("我不會叫");} }

下面就是Duck類和具體不同種類的鴨子了
Dunk類

// 鴨子抽象類 public abstract class Duck {// Duck方法關(guān)聯(lián)于FlyBehavior和QuackBehavior接口// 這兩個(gè)接口同樣聚合在Dunk類中private FlyBehavior flyBehavior; // 鴨子飛屬性private QuackBehavior quackBehavior; // 鴨子叫屬性// 抽象方法:顯示什么鴨子public abstract void display();// 飛行的方法public void performFly() {flyBehavior.fly();}// 叫的方法public void performQuack() {quackBehavior.quack();}// 設(shè)置飛行的方法public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}// 設(shè)置叫的方法public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;} } // 紅頭鴨 public class RedheadDuck extends Duck{public RedheadDuck() {this.setFlyBehavior(new FlyWithWings());this.setQuackBehavior(new Quack());}@Overridepublic void display() {System.out.println("我是一只紅頭鴨!!");} } // 綠頭鴨 public class MallardDuck extends Duck {// 默認(rèn)構(gòu)造方法,目的是給父類兩個(gè)接口實(shí)例化對象public MallardDuck() {this.setFlyBehavior(new FlyWithWings()); // 用翅膀飛this.setQuackBehavior(new Quack()); // 嘎嘎叫}@Overridepublic void display() {System.out.println("我是一只綠頭鴨!!");} } // 橡皮鴨 public class RubberDuck extends Duck{public RubberDuck() {this.setFlyBehavior(new FlyNoWay()); // 不能飛this.setQuackBehavior(new Squeak()); // 吱吱叫}@Overridepublic void display() {System.out.println("我是一只橡皮鴨!!");} } // 模型鴨 public class ModelDuck extends Duck{public ModelDuck() {this.setFlyBehavior(new FlyWithRocket()); // 火箭噴射器飛this.setQuackBehavior(new MuteQuack()); // 不會叫}@Overridepublic void display() {System.out.println("我是一只模型鴨!!");} }

終于實(shí)現(xiàn)了所有的功能,張三懷著忐忑寫了一個(gè)測試代碼:

// 測試系統(tǒng) public class MiniDuckSimulator {public static void main(String[] args) {Duck mallardDuck = new MallardDuck(); // 綠頭鴨mallardDuck.display();mallardDuck.performFly();mallardDuck.performQuack();System.out.println("-----------------");Duck redheadDuck = new RedheadDuck();redheadDuck.display();redheadDuck.performFly();redheadDuck.performQuack();System.out.println("-----------------");Duck rubberDuck = new RubberDuck();rubberDuck.display();rubberDuck.performFly();rubberDuck.performQuack();System.out.println("-----------------");Duck modelDuck = new ModelDuck();modelDuck.display();modelDuck.performFly();modelDuck.performQuack();modelDuck.setFlyBehavior(new FlyNoWay()); // 動(dòng)態(tài)改變對象行為modelDuck.performFly();} }

輸出結(jié)果:

我是一只綠頭鴨!! I'm flying!!! 嘎嘎嘎 ----------------- 我是一只紅頭鴨!! I'm flying!!! 嘎嘎嘎 ----------------- 我是一只橡皮鴨!! I can't flying 吱吱吱 ----------------- 我是一只模型鴨!! Fly with a rocket!! 我不會叫 I can't flying

第二版的代碼完美的實(shí)現(xiàn)了所有的功能,并且代碼的彈性和拓展性都很不錯(cuò),張三想:升職加薪這不就穩(wěn)穩(wěn)地嘛;

李四這時(shí)說:這個(gè)代碼就是用到了設(shè)計(jì)模式之一 ——策略模式,想要升職加薪,光會這一個(gè)設(shè)計(jì)模式可不行,后面的路還長著呢;

體會到了設(shè)計(jì)模式的好處,張三下定決心好好學(xué)習(xí)設(shè)計(jì)模式;

總結(jié)

上面張三的例子可以看出策略模式的好處

  • 不需要許多 if …else或者switch 判斷語句
  • 代碼可拓展性好
  • 符合開閉原則,便于維護(hù)

同樣策略模式需要注意:每添加一個(gè)策略就要增加一個(gè)類,當(dāng)策略過多是會導(dǎo)致策略類膨脹


其實(shí)這個(gè)例子中還用到了一個(gè)設(shè)計(jì)原則: 多用組合和聚合,少用泛化(繼承)
這里總結(jié)一下文中提到的 三種設(shè)計(jì)原則:
  • 封裝變化的行為
  • 面向接口編程,不針對實(shí)現(xiàn)編程
  • 多用組合聚合,少用繼承

當(dāng)然這三個(gè)只是這里用到的,對于設(shè)計(jì)原則可不止這三種,后面會一 一介紹;

再重新看一下策略模式的定義:
策略模式是對算法的包裝,是把使用算法的責(zé)任和算法本身分割開來,委派給不同的對象管理。
策略模式結(jié)構(gòu)圖:

其實(shí)策略模式在Java源碼中也有體現(xiàn),簡單舉個(gè)例子:Constructor就用到了策略模式,我們可以通過實(shí)現(xiàn)它來創(chuàng)造不同的排序規(guī)則,感興趣可以看看源碼體驗(yàn)一下;

當(dāng)然一個(gè)例子不足以讓你會用策略模式,想要真正的掌握還是需要大量的練習(xí)和實(shí)踐,希望這篇文章能給你帶來一些啟發(fā)!

點(diǎn)

總結(jié)

以上是生活随笔為你收集整理的设计模式(一)————策略模式(张三的故事??)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。