HeadFirst設計模式(一)策略者模式

来源:https://www.cnblogs.com/zs10/archive/2019/07/03/11123533.html
-Advertisement-
Play Games

最近在看HeadFirst設計模式一書,作為一個半路出家的程式員,感覺很多東西需要學習,學習的路程中有些東西學了當時覺得理解了,但日常工作中沒有使用到漸漸的自己就忘記了。 上面就是寫者系列的博客的原因,主要是為了鞏固知識,忘記在那個博主那邊看過這麼一句話,知識學了後總結了才變成自己的。 策略者模式 ...


最近在看HeadFirst設計模式一書,作為一個半路出家的程式員,感覺很多東西需要學習,學習的路程中有些東西學了當時覺得理解了,但日常工作中沒有使用到漸漸的自己就忘記了。----------------------上面就是寫者系列的博客的原因,主要是為了鞏固知識,忘記在那個博主那邊看過這麼一句話,知識學了後總結了才變成自己的。

 

策略者模式----定義了演算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶。

當然這隻是理論的東西,說實話我現在都沒理解這個理論是啥,下麵我用代碼和比較直觀的語言給大家介紹一下策略者模式,希望能有所收穫。

模擬情節:

    Joe上班的公司有一套鴨子游戲的系統:SimUDuck,游戲中有各種鴨子,一邊游泳,一邊呱呱叫。此系統使用了標準的OO(面向對象)技術。設計一個鴨子的超類(也就是父類-Duck),並讓各種鴨子繼承此超類。

此時由於需求的改變需要增加飛行的功能,Joe直接在超類中添加fly()方法:

 1   public abstract class Duck
 2     {
 3         public  void quack()
 4         {
 5             Console.WriteLine("我會叫");
 6         }
 7 
 8         public void swim()
 9         {
10             Console.WriteLine("我會游泳");
11         }
12 
13         public void fly()
14         {
15             Console.WriteLine("我會飛");
16         }
17         public abstract void display();
18     }
19 
20     public class MallardDuck : Duck
21     {
22         public override  void  display()
23         {
24             Console.WriteLine("我是一隻綠頭鴨");
25         }
26 
27     }
28     public class ModelDuck : Duck
29     {
30         public override void display()
31         {
32             Console.WriteLine("我是一隻模型鴨子");
33         }
34     }
35     public class RedheadDuck : Duck
36     {
37 
38         public override  void display()
39         {
40             Console.WriteLine("我是一隻紅頭鴨");
41         }
42     }
43 
44     class Program
45     {
46         static void Main(string[] args)
47         {
48             Duck  modelDuck=new ModelDuck();
49             modelDuck.quack();
50             modelDuck.display();
51             modelDuck.fly();
52             Console.ReadLine();
53         }
54     }

此時問題就來了,模型鴨子(ModelDuck)由於也是繼承於超類也具備了飛行的功能,顯然繼承在這裡並不是一個很好的解決方案。這裡在超類中用到了抽象的方法,有個小技巧對於新學者來說經常搞不清抽象方法的語法,記住抽象方法是沒有身體的,也就是說他是沒有方法體的。

這時候就不能用繼承了,這時候有人提出把fly()和quack()方法提到介面IFlyable與IQuackable中,這樣可以飛行的鴨子實現介面,不可以飛行的模型鴨子就不實現介面,錶面上看來是滿足了要求,但這樣的話不僅僅fly()在每個可以飛行的鴨子類中都要實現一邊造成代碼重覆太多,如果要將可以飛行的鴨子的飛行行為稍微改動一下的話,那麼面臨的將是災難,沒給方法你都需要改動一下。

此時,你是否期望一種設計模式能來救你於苦海,這裡我先賣一個關子,接下來我會用老方法找出一個解決之道,“採用良好的OO軟體設計原則”;

通過上面的例子的一步步學習,我想我們應該知道繼承並不能很好的解決問題,因為鴨子的行為在子類中是不斷地變化,所以讓所有的子類都有這些行為是不恰當的。介面IFlyable與IQuackable中一開始看起來還挺不錯,解決了問題但是由於介面不具備實現代碼,所以實現介面無法達到代碼的復用。這意味者你無論何時要修改某個行為,都會造成你需要修改所有實現該介面的類中修改他們,這樣很容易造成錯誤。有個設計原則能很好的解決此問題,

設計原則:找出應用中可能需要變化的部分,把他們獨立出來,不要和那些不需要變化的代碼混合在一起。


這是我們介紹的第一個設計原則,在後面的學習中我還會介紹被的設計原則,我們先對著上面的例子看看那部分是改變的,都的鴨子的行為是不斷的變化的我們需要把行為單獨提出來,我們知道Duck類內的fly()和quack()會隨著鴨子的改變而不斷的改變,為了把這兩個行為從Duck類中分開,我們將把他們從Duck類中取出來,建立一組新類來代表每個行為。

設計鴨子的行為

如何設計那組實現飛行和呱呱叫的行為呢?

首先我們希望一切具有彈性,一開始的鴨子系統正是因為沒有彈性,才讓我們走上這條路,我們還行能夠‘指定’行為到鴨子的實例。我們想要產生一個新的綠鴨子的實例的時候,並指定特定‘類型’的飛行行為給它,乾脆順便讓鴨子的行為可以動態的改變好了。換句話說,我們應該在鴨子類中包含設定行為的方法,這樣就可以在‘運行時’動態地‘改變’綠頭鴨子的行為。

有了這些目標的需求,接著我們來看我們的第二給設計原則

設計原則:針對介面編程,而不是針對實現編程

從現在開始,鴨子的行為將被放在分開的類中,此類專門提供某行為介面的實現。這樣的話和之前的做法有了明顯的不同,之前的做法是:行為來自Duck超類的具體實現,或者繼承某給介面並由子類自行實現行為。這兩種做法都是依賴於‘實現’造成我們的系統不再有彈性,很難更改別的代碼。

在我們新的實現中,鴨子的子類將使用介面(IFlyBehavior與IQuackBehavior)所表示的行為,也就是實現此介面的行為類(FlyNoWay等)具體的行為的實現現在編寫在行為類中。這樣的設計就使得我們的系統具備的彈性。

這樣的設計,可以讓飛行和呱呱叫的動作被其他的對象復用,因為這些行為已經與鴨子類無關了。而我們也可以新增一些行為,不會影響到既有的行為類,也不會影響‘使用’到飛行行為的鴨子類。

整合鴨子的行為

關鍵在於,鴨子現在會將飛行和呱呱叫的動作‘委托’(delegate)別人處理,而不是使用定義在Duck類(或子類)內的呱呱叫和飛行方法。

做法的這樣的:

①首先,在Duck類中‘加入兩個實例變數’,分別為”iFlyBehavior“與”IQuackBehavior“,聲明為介面類型(而不是具體實現介面的類的類型)每個鴨子對象通過動態的實現這些變數以在運行時引用正確的行為類型。

我們用兩個相似的方法performFly和performQuack來取代Duck類中fly()與quack()。稍後你就知道原因了。

②現在,我們來實現

 

performQuack();public class Duck
{ IQuackBehavior iQuackBehavior;//每隻鴨子都會引用實現IQuackbehavior介面的對象。 public void performQuack() { iQuackBehavior.quack();//鴨子對象不親自處理呱呱叫行為,而是委托給iQuackBehavior引用的對象。

   }
}

 

Duck對象只要叫iQuackBehavior對象去呱呱叫就可以了,在這部分的代碼中,我們不在關心iQuackBehavior介面的對象到底是什麼,我們只關心該對象知道如何進行呱呱叫就夠了。

3.現在關心如何實現iQuackBehavior與iFlyBehavior的實例變數,看看具體繼承Duck類的子類是怎麼實現的吧

    public class ModelDuck : Duck
    {
        public ModelDuck()
        {
            iFlyBehaviro = new DuckNoFly();
            iQueackBehaviro = new DuckNoQueck();
        }           
        public override void Display()
        {
            Console.WriteLine("我是一隻木頭鴨子");
        }
    }

 在ModelDuck實例化時,它的構造器會把繼承來的iFlyBehavior和iQuackBehavior實例變數初始化成DuckNoFly與DuckNoQuack類型的新實例。(DuckNoFly與DuckNoQuack是介面的具體實現)

現在只剩下一個在運行時動態該百年鴨子的行為的功能還未實現,我們上面是在具體鴨子類的構造器內進行更改鴨子的行為的,如果換一個地方將更改鴨子的行為提取到Duck超類中,那麼只要繼承自該超類的鴨子派生類不都具備了動態更改行為的能力了嗎?

在Duck類中,加入兩個新方法:

    public void setPerformFly(IFlyBehaviro fb)
        {
            iFlyBehaviro = fb;
        }
        public void setPerformQuack(IQuackBehaviro qb)
        {
            iQueackBehaviro = qb;
        }

  從此以後,我們可以‘隨時’調用者兩個方法改變鴨子的行為。下麵我貼上更改後的全部代碼:

 public abstract class Duck
    {
        public IFlyBehaviro iFlyBehaviro;
        public IQuackBehaviro iQueackBehaviro;
        public abstract void Display();

        public void Swim()
        {

        }
        public void performFly()
        {
            iFlyBehaviro.fly();
        }
        public void performQuack()
        {
            iQueackBehaviro.quack();
        }
        public void setPerformFly(IFlyBehaviro fb)
        {
            iFlyBehaviro = fb;
        }
        public void setPerformQuack(IQuackBehaviro qb)
        {
            iQueackBehaviro = qb;
        }
    }
    public class ModelDuck : Duck
    {
        public ModelDuck()
        {
            iFlyBehaviro = new DuckNoFly();
            iQueackBehaviro = new DuckNoQueck();
        }           
        public override void Display()
        {
            Console.WriteLine("我是一隻木頭鴨子");
        }
    }

   public interface IFlyBehaviro
    {
         void fly();
    }
    public interface IQuackBehaviro
    {
        void quack();
    }
    public class DuckNoFly : IFlyBehaviro
    {
        public void fly()
        {
            Console.WriteLine("我不會飛");
        }
    }
    public class DuckCanFlay:IFlyBehaviro
    {
        public void fly()
        {
            Console.WriteLine("我會飛");
        }
    }
    public class DuckNoQueck : IQuackBehaviro
    {
        public void quack()
        {
            Console.WriteLine("我不會叫");
        }
    }
    public class DuckGuaGuaQueck:IQuackBehaviro
    {
        public void quack()
        {
            Console.WriteLine("呱呱叫");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
           
            Duck modelDuck = new ModelDuck();
            modelDuck.Display();
            modelDuck.performFly();
            modelDuck.performQuack();

            modelDuck.setPerformFly(new DuckCanFlay());
            modelDuck.performFly();
            Console.ReadLine();
        }
    }

  封裝行為的大局觀

我們已經深入研究了鴨子模擬器的設計,該是看看整體的格局了:

這是重新設計後的類結構,你所期望的一切都有:鴨子繼承Duck,飛行行為實現IFlyBehavior介面,呱呱叫行為實現IQuackBehavior介面。請特別註意類之間的”關係“,關係可以是IS-A(是一個)、HAS-A(有一個)或IMPLEMENTS(實現)。

”有一個“可能比”是一個“更好

”有一個“關係相當有趣:每一個鴨子都有一個IFlyBehavior和一個IQuackBehavior,好將飛行和呱呱叫的委托給它們代為處理。

當你將兩個類結合起來使用,如同本例一般,這就是組合。這種做法和”繼承“不同的地方在於,鴨子的行為不是通過繼承來的,而是和適當的行為對象”組合“來的。

這是一個很重要的技巧,其實也是我們介紹的第三給設計原則:

設計原則:多用組合,少用繼承。

如你所見,使用組合建立系統具有很大的彈性,不僅可以將演算法組封裝成類,更可以”在運行時動態的改變行為“,只要組合的行為對象符合正確的介面標準即可。”組合“被廣泛的應用在很多設計模式中,後面你會經常發現它的身影。

至此,這就是我們學習的第一個模式了。

在上面的內容中我們需要到了幾項內容:

OO基礎:抽象,封裝,多態,繼承。

OO原則:封裝變化;多用組合,少用繼承;針對介面編程,不針對實現編程。

OO模式:策略模式----定義演算法族,分別封裝起來,讓他們直接可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶。

初衷,只是想把學習的設計模式自己總結成博客記錄下來,但是好像有點偏離初心更像是一個教別人怎麼理解策略者模式了,水平有限,寫的不是很好,對本內容感興趣的讀者,推薦你閱讀《Head First設計模式》一書,該書很生動的講解了設計模式。我上面所寫的內容也是基於此書。最後感謝您的閱讀。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.如果上一頁是靜態頁面,可以用 history.go(-1)方法; go() 方法可載入歷史列表中的某個具體的頁面。 該參數可以是數字,使用的是要訪問的 URL 在 History 的 URL 列表中的相對位置。(-1上一個頁面,1前進一個頁面)。或一個字元串,字元串必須是局部或完整的URL,該函 ...
  • 最近在做項目時,碰到 safari 瀏覽器不支持location跳轉問題,針對此問題,可以通過 js 模擬點擊時間來解決,代碼如下: ...
  • 最近使用layui的框架時,發現table插件不支持鍵盤快捷鍵切換單元格,花了點時間實現此功能。 分享給有需要的朋友們~~~ 效果圖 代碼: 1.支持 enter,上,下,右鍵 切換單元格,支持隱藏列跳過切換。註:單元格必須開啟了 edit:text 模式,才支持鍵盤切換。使用方法:1.在需要啟用此 ...
  • 跟我學SpringCloud | 第四篇:熔斷器Hystrix 1. 熔斷器 服務雪崩 在正常的微服務架構體系下,一個業務很少有隻需要調用一個服務就可以返回數據的情況,這種比較常見的是出現在demo中,一般都是存在調用鏈的,比如A B C D,如果D在某一個瞬間出現問題,比如網路波動,io偏高,導致 ...
  • 本文來講解一下兩個結構比較相似的行為設計模式:策略模式和狀態模式。兩者單獨的理解和學習都是比較直觀簡單的,但是實際使用的時候卻並不好實踐,算是易學難用的設計模式吧。這也是把兩者放在一起介紹的原因,經過對比和實例介紹,相信應該會一些比較深刻的感知。最後在結合個人的體會簡單聊一下對這兩個模式的一些看法。 ...
  • 小孩玩王者榮耀上癮,偷偷充值了6000多元,我讓騰訊游戲根據實名登記的身份證信息禁止玩游戲,這也是國家網路游戲管理辦法中要求的,禁止未成年人接觸不適宜的游戲,騰訊說做不到。這到底是騰訊游戲系統架構問題還是不作為? ...
  • 官網:www.fhadmin.org 特別註意: Springboot 工作流 前後分離 + 跨域 版本 (許可權控制到菜單和按鈕) 後臺框架:springboot2.1.2+ activiti6.0.0+ mybaits+maven+介面 前端頁面:html +vue.js 形式 jquery aj ...
  • 具有“魔性”的通用軟體開發框架,僅有5個普通的控制器類,卻響應著任何複雜的業務場景。其超前的思路、原生態的實現方式為有個性化思路的研發者提供了高度靈活的擴展空間。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...