1. 程式人生 > >C#基礎系列——委託和設計模式(二)

C#基礎系列——委託和設計模式(二)

前言:前篇 C#基礎系列——委託實現簡單設計模式 簡單介紹了下委託的定義及簡單用法。這篇打算從設計模式的角度去解析下委託的使用。我們知道使用委託可以實現物件行為(方法)的動態繫結,從而提高設計的靈活性。上次說過,方法可以理解為委託的例項,站在方法的層面,委託例項的一個非常有用的特性是它既不知道,也不關心其封裝方法所屬類的詳細資訊,對它來說最重要的是這些方法與該委託的引數和返回值的相容性。即只要方法的返回型別和引數表是相同的,則方法與委託型別相容,方法的名稱及方法所屬類等資訊委託是不關心的。有一定程式設計經驗的大俠們肯定都接觸過設計模式,其實設計模式大多數都是面向物件多型特性的體現,通過重寫子類方法去展現不同的設計需求,這樣看,既然是方法重寫,那麼方法的引數型別和返回值型別肯定是一致的,這是不是和委託的例項十分相似,這樣說來,我們通過多型去實現的設計模式是否可以用委託的形式去代替。博主覺得,為了更好的理解委託,可以從這方面著手試試。。。

  時間過得真快,轉眼C#基礎系列已經寫了8篇隨筆了,不管寫的好不好,博主都會繼續,做事要有始有終嘛~~前天在園子看到一篇文章目錄的博文,這裡將博主的系列文章也列一個目錄出來,這樣以後找起來也方便。

此篇簡單抽取了幾個設計模式分別按照多型和委託的方式去實現,當然這裡的重點並不是講設計模式,而是為了使讀者更好地理解委託。所以設計模式的很多細節,本篇可能會略過。

一、簡單工廠模式:本篇就藉助計算器的例子加以說明。

1、多型實現簡單工廠模式。

   class Program2
    {
        static void Main(string[] args)
        {
            
//1.使用多型實現簡單工廠模式 int x = 8, y = 2; var iRes1 = GetObject("+").Compute(x, y); var iRes2 = GetObject("-").Compute(x, y); var iRes3 = GetObject("*").Compute(x, y); var iRes4 = GetObject("/").Compute(x, y); Console.WriteLine(iRes1); Console.WriteLine(iRes2); Console.WriteLine(iRes3); Console.WriteLine(iRes4); Console.ReadKey(); }
static Calculator GetObject(string type) { Calculator oRes = null; switch (type) { case "+": oRes = new Add(); break; case "-": oRes = new Subtract(); break; case "*": oRes = new Multiply(); break; case "/": oRes = new Divide(); break; } return oRes; } } public class Calculator { public virtual int Compute(int x, int y) { return 0; } } public class Add : Calculator { public override int Compute(int x, int y) { return x + y; } } public class Subtract : Calculator { public override int Compute(int x, int y) { return x - y; } } public class Multiply : Calculator { public override int Compute(int x, int y) { return x * y; } } public class Divide : Calculator { public override int Compute(int x, int y) { if (y == 0) { return 0; } return x / y; } }

程式碼應該很容易看懂,直接通過方法的重寫去實現,在此就不過多講解。

2、委託方式實現簡單工廠模式。

class Program2
    {
        
        static void Main(string[] args)
        {
            #region 2.委託實現簡單工廠模式
            int x = 8, y = 2;
            var oCalculator = new Calculator();
            var iRes1 = oCalculator.Compute(x, y, oCalculator.Add);//將方法作為引數傳下去
            var iRes2 = oCalculator.Compute(x, y, oCalculator.Subtract);
            var iRes3 = oCalculator.Compute(x, y, oCalculator.Multiply);
            var iRes4 = oCalculator.Compute(x, y, oCalculator.Divide);

            Console.WriteLine(iRes1);
            Console.WriteLine(iRes2);
            Console.WriteLine(iRes3);
            Console.WriteLine(iRes4); 
            #endregion

            Console.ReadKey();
        }
    }

    public delegate int DelegateCalculator(int x, int y);

    public class Calculator
    {
     //將方法的例項傳遞進來,在Compute方法裡面執行
public int Compute(int x, int y, DelegateCalculator calculator) { return calculator(x, y); } public int Add(int x, int y) { return x + y; } public int Subtract(int x, int y) { return x - y; } public int Multiply(int x, int y) { return x * y; } public int Divide(int x, int y) { if (y == 0) { return 0; } return x / y; } }

這裡需要定義四個實現方法Add、Subtract、Multiply、Divide,而不用在意這四個方法在哪個類下面,只要這四個方法的的引數和返回值和委託的定義保持一致即可。這也驗證了上面說的 “站在方法的層面,委託例項的一個非常有用的特性是它既不知道,也不關心其封裝方法所屬類的詳細資訊,對它來說最重要的是這些方法與該委託的引數和返回值的相容性” 。兩種方式得到的結果是相同的:

二、觀察者模式:觀察者模式最典型的場景就是訂閱者和訂閱號的場景

1、純多型方式實現觀察者模式:這種程式碼園子裡面非常多。

class Program3
    {
        static void Main(string[] args)
        {
            // 具體主題角色通常用具體自來來實現
            ConcreteSubject subject = new ConcreteSubject();

            subject.Attach(new ConcreteObserver(subject, "Observer A"));
            subject.Attach(new ConcreteObserver(subject, "Observer B"));
            subject.Attach(new ConcreteObserver(subject, "Observer C"));

            subject.SubjectState = "Ready";
            subject.Notify();

            Console.Read();
        }
    }

    //抽象主題類
    public abstract class Subject
    {
        private IList<Observer> observers = new List<Observer>();

        /// <summary>
        /// 增加觀察者
        /// </summary>
        /// <param name="observer"></param>
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }

        /// <summary>
        /// 移除觀察者
        /// </summary>
        /// <param name="observer"></param>
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        /// <summary>
        /// 向觀察者(們)發出通知
        /// </summary>
        public void Notify()
        {
            foreach (Observer o in observers)
            {
                o.Update();
            }
        }
    }

    //具體主題類
    public class ConcreteSubject : Subject
    {
        private string subjectState;

        /// <summary>
        /// 具體觀察者的狀態
        /// </summary>
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }

    //抽象觀察者類
    public abstract class Observer
    {
        public abstract void Update();
    }



    //具體觀察者
    public class ConcreteObserver : Observer
    {
        private string observerState;
        private string name;
        private ConcreteSubject subject;

        /// <summary>
        /// 具體觀察者用一個具體主題來實現
        /// </summary>
        public ConcreteSubject Subject
        {
            get { return subject; }
            set { subject = value; }
        }

        public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }

        /// <summary>
        /// 實現抽象觀察者中的更新操作
        /// </summary>
        public override void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
        }
    }

可以看到雖然已經很好的實現了觀察者Observer 和主題Subject之間的分離。但是Subject的內部還是有對觀察者的呼叫:

public void Notify()
{
     foreach (Observer o in observers)
     {
          o.Update();
     }
}

2、多型和委託實現觀察者模式。

   class Program3
    {
        static void Main(string[] args)
        {
            // 具體主題角色通常用具體自來來實現
            ConcreteSubject subject = new ConcreteSubject();

            //傳入的只是觀察者的通過方法。
            subject.Attach(new ConcreteObserver(subject, "Observer A").Update);
            subject.Attach(new ConcreteObserver(subject, "Observer B").Update);
            subject.Attach(new ConcreteObserver(subject, "Observer C").Update);

            subject.SubjectState = "Ready";
            subject.Notify();

            Console.Read();
        }
    }

    public delegate void ObserverDelegate();

    //抽象主題類
    public abstract class Subject
    {
        public ObserverDelegate observedelegate;

        /// <summary>
        /// 增加觀察者
        /// </summary>
        /// <param name="observer"></param>
        public void Attach(ObserverDelegate observer)
        {
            observedelegate += observer;
        }

        /// <summary>
        /// 移除觀察者
        /// </summary>
        /// <param name="observer"></param>
        public void Detach(ObserverDelegate observer)
        {
            observedelegate -= observer;
        }

        /// <summary>
        /// 向觀察者(們)發出通知
        /// </summary>
        public void Notify()
        {
            if (observedelegate != null)
            {
                observedelegate();
            }
        }
    }

    //具體主題類
    public class ConcreteSubject : Subject
    {
        private string subjectState;

        /// <summary>
        /// 具體觀察者的狀態
        /// </summary>
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }

    //具體觀察者
    public class ConcreteObserver
    {
        private string observerState;
        private string name;
        private ConcreteSubject subject;

        /// <summary>
        /// 具體觀察者用一個具體主題來實現
        /// </summary>
        public ConcreteSubject Subject
        {
            get { return subject; }
            set { subject = value; }
        }

        public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }

        /// <summary>
        /// 實現抽象觀察者中的更新操作
        /// </summary>
        public void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
        }
    }

得到結果:

這樣設計的優勢:

(1)將通知的方法Update通過委託的形式傳入主題物件。這樣主題物件Subject就完全和觀察者隔離。更好地實現了低耦合。

(2)減少了觀察者抽象類的定義。使整個設計更加精簡。

(3)如果將設計更進一步,觀察者這邊自定義delegate void ObserverDelegate()這種型別的方法。比如需要執行Update()方法之後還要記錄一個日誌的操作。如:

//具體觀察者
    public class ConcreteObserver
    {
        private string observerState;
        private string name;
        private ConcreteSubject subject;

        /// <summary>
        /// 具體觀察者用一個具體主題來實現
        /// </summary>
        public ConcreteSubject Subject
        {
            get { return subject; }
            set { subject = value; }
        }

        public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }

        /// <summary>
        /// 實現抽象觀察者中的更新操作
        /// </summary>
        public void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
        }

        public void Log()
        {
            Console.WriteLine("Log:Update方法執行完成");
        }
    }

那麼在客戶端呼叫時只需要將Log方法以委託的形式傳入即可:

static void Main(string[] args)
        {
            // 具體主題角色通常用具體自來來實現
            ConcreteSubject subject = new ConcreteSubject();

            //傳入的只是觀察者的通過方法。
            var obj = new ConcreteObserver(subject, "Observer A");
            subject.Attach(obj.Update);
            subject.Attach(obj.Log);

            subject.SubjectState = "Ready";
            subject.Notify();

            Console.Read();
        }

是不是顯得更靈活一點。如果是純多型的方式,由於Subject裡面指定了呼叫Update()方法,所以當需要增加Log方法的時候程式碼的改變數要大。

三、模板方法模式,這裡就以裝置採集為例來進行說明:

1、多型實現模板方法模式:

    class Program4
    {
        static void Main(string[] args)
        {
            var oTem1 = new DeviceMML();
            oTem1.Spider();
            Console.WriteLine("");
            var oTem2 = new DeviceTL2();
            oTem2.Spider();

            Console.ReadKey();
        }
    }

    public abstract class TempleteDevice
    {
        // 模板方法,不要把模版方法定義為Virtual或abstract方法,避免被子類重寫,防止更改流程的執行順序
        public void Spider()
        {
            Console.WriteLine("裝置採集開始");
            this.Login();
            this.Validation();
            this.SpiderByType1();
            this.SpiderByType2();
            this.LoginOut();

            Console.WriteLine("裝置採集結束");
        }

        // 登陸
        public void Login()
        {
            Console.WriteLine("登陸");
        }

        // 驗證
        public void Validation()
        {
            Console.WriteLine("驗證");
        }

        // 採集
        public abstract void SpiderByType1();
        public abstract void SpiderByType2();

        // 登出
        public void LoginOut()
        {
            Console.WriteLine("登出");
        }
    }

    //MML型別的裝置的採集
    public class DeviceMML : TempleteDevice
    {
        public override void SpiderByType1()
        {
            Console.WriteLine("MML型別裝置開始採集1");
            //.......
        }

        public override void SpiderByType2()
        {
            Console.WriteLine("MML型別裝置開始採集2");
        }
    }

    //TL2型別裝置的採集
    public class DeviceTL2 : TempleteDevice
    {
        public override void SpiderByType1()
        {
            Console.WriteLine("TL2型別裝置開始採集1");
            //.......
        }

        public override void SpiderByType2()
        {
            Console.WriteLine("TL2型別裝置開始採集2");
        }
    }

父類裡面的非abstract方法都是模板方法,也就是子類公用並且不可以重寫的方法。SpiderType1和SpiderType2是需要子類重寫的方法。模板方法模式在抽象類中定義了演算法的實現步驟,將這些步驟的實現延遲到具體子類中去實現,從而使所有子類複用了父類的程式碼,所以模板方法模式是基於繼承的一種實現程式碼複用的技術。

2、使用委託改寫後:

    class Program4
    {
        static void Main(string[] args)
        {
            var oTem1 = new TempleteDevice(DeviceMML.SpiderByType1, DeviceMML.SpiderByType2);
            oTem1.Spider();

            Console.WriteLine("");

            var oTem2 = new TempleteDevice(DeviceTL2.SpiderByType1, DeviceTL2.SpiderByType2);
            oTem2.Spider();
            Console.ReadLine();
        }
    }

    public delegate void DeviceDelegate();

    public class TempleteDevice
    {
        public DeviceDelegate oDelegate;

        public TempleteDevice(params DeviceDelegate[] lstFunc)
        {
            foreach (var oFunc in lstFunc)
            {
                oDelegate += oFunc;
            }
            
        }

        // 模板方法,不要把模版方法定義為Virtual或abstract方法,避免被子類重寫,防止更改流程的執行順序
        public void Spider()
        {
            Console.WriteLine("裝置採集開始");
            this.Login();
            this.Validation();
            if (oDelegate != null)
            {
                oDelegate();
            }
            this.LoginOut();

            Console.WriteLine("裝置採集結束");
        }

        // 登陸
        public void Login()
        {
            Console.WriteLine("登陸");
        }

        // 驗證
        public void Validation()
        {
            Console.WriteLine("驗證");
        }

        // 登出
        public void LoginOut()
        {
            Console.WriteLine("登出");
        }
    }

    //MML型別的裝置的採集
    public class DeviceMML
    {
        public static void SpiderByType1()
        {
            Console.WriteLine("MML型別裝置開始採集1");
            //.......
        }

        public static void SpiderByType2()
        {
            Console.WriteLine("MML型別裝置開始採集2");
        }
    }

    //TL2型別裝置的採集
    public class DeviceTL2
    {
        public static void SpiderByType1()
        {
            Console.WriteLine("TL2型別裝置開始採集1");
            //.......
        }

        public static void SpiderByType2()
        {
            Console.WriteLine("TL2型別裝置開始採集2");
        }
    }

得到結果:

優化模板方法模式的意義:

(1)解除了子類和父類之間的繼承關係,更好地實現了物件間的低耦合。

(2)採用委託可以動態實現方法的組合,這種方式更加靈活,子類可以更加靈活的設計不同部分的方法。然後方法的數量通過params來傳遞,方法的數量沒有什麼嚴格的限制。

當然其他設計模式也可以使用委託去優化設計,博主在這裡就暫時只分享這三種模式的異同。總的來說,委託不可能代替多型去實現各種模式,但是它和多型聯合起來使用可以實現更加靈活的設計。通過這兩篇下來,不知道你是否對委託有點感覺了呢,委託這東西,重在實戰,就像游泳一樣,如果不用那麼幾次,你永遠也不可能學會。以上只是博主個人的理解,可能很多方便沒有考慮得那麼全面,希望各位園友拍磚斧正~~

相關推薦

C#基礎系列——委託設計模式

前言:前篇 C#基礎系列——委託實現簡單設計模式 簡單介紹了下委託的定義及簡單用法。這篇打算從設計模式的角度去解析下委託的使用。我們知道使用委託可以實現物件行為(方法)的動態繫結,從而提高設計的靈活性。上次說過,方法可以理解為委託的例項,站在方法的層面,委託例項的一個非常有用的特性是它既不知道,也不關心其封裝

C#基礎系列委託設計模式2

前言:這篇打算從設計模式的角度去解析下委託的使用。我們知道使用委託可以實現物件行為(方法)的動態繫結,從而提高設計的靈活性。上次說過,方法可以理解為委託的例項,站在方法的層面,委託例項的一個非常有用的特性是它既不知道,也不關心其封裝方法所屬類的詳細資訊,對它來說最重

Java程式設計師從笨鳥到菜鳥之三十大話設計模式設計模式分類三種工廠模式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

c#委託lambda表示式

Lambda表示式 Lambda表示式是一種可用於建立委託或表示式目錄樹型別的匿名函式。通過使用Lambda表示式,可以寫入可作為引數傳遞或作為函式呼叫值返回的本地函式。 若要建立Lambda表示式,需要在Lambda運算子=>左側輸入引數(如果有),

設計模式:代理模式委託模式

代理模式 組成: 抽象角色:通過介面或者抽象類宣告真實角色實現的業務方法 代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。 真實角色:實現抽象角色,定義真實角色所需要實現的業務邏輯,供代理角色呼叫 代理的基本作用

設計模式:適配器模式外觀模式

mov col 出國旅遊 註意 charge err 類圖 rri 方式 適配器模式 一、出國必備適配器 隨著中國經濟的快速發展,我們也越來越有錢了。出國旅遊也不是什麽新鮮事了。泰國是中國人最愛去的旅遊地之一。當我們程序員去泰國旅遊時,可能會帶著筆記本電腦去,以便隨時可

架構設計設計模式

1,談談你對android設計模式的理解2,MVC MVP MVVM原理和區別3,你所知道的設計模式有哪些?4,專案中常用的設計模式5,手寫生產者/消費者模式6,寫出觀察者模式的程式碼7,介面卡模式,裝飾者模式,外觀模式的異同你?8,用到的一些開源框架,介紹一個看過原始碼的,

設計模式: 工廠模式

dem blank hibernate 執行 oid code 做出 void actor 工廠模式 工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。 在工廠模式中,我們在創建

java設計模式工廠模式

額外 mod 通過反射 pat 擴展 簡單實現 需要 factory actor   工廠模式是最常見的實例化對象的模式,用來替代new操作。采用這種模式創建對象會有一些額外的操作,但他會帶給系統更大的擴展性和更少的修改量。典型的應用spring bean容器。下面簡單實現

設計模式---工廠方法模式

ack cto sys alt 修改 spa 抽象類 .com desc 1、簡介:工廠方法模式是類的創建模式,又叫虛擬構造子模式或是多態性工廠模式,它的實現方式是創建一個工廠接口,將實際創建對象的的工作轉移到工廠子類中,在系統的擴展中,可以在不修改工廠角色的情況下引進新的

設計模式觀察者監聽者模式

blog virtual ack memory message efault share default IT 基於MAP線程安全的觀察者監聽者模式 1 #include<map> 2 #include<string> 3 #i

設計模式—— 工廠方法

源碼下載 工廠方法模式 ati \n end 註意 con 流程 pie 工廠方法 模式簡介 工廠方法模式(Factory Method Pattern)又被稱為工廠模式,也叫虛構造器(Virtual Constructor)模式或多態工廠(Polymorphic Fact

常用的JavaScript設計模式Factory(工廠)模式

asc 我們 對象實例 actor 通過 汽車 對象 UNC cti Factory通過提供一個通用的接口來創建對象,同時,我們還可以指定我們想要創建的對象實例的類型。 假設現在有一個汽車工廠VehicleFactory,支持創建Car和Truck類型的對象實例,現在需要通

Java設計模式

this main 一個 return cut 建表 演示 算法 變化 行為類模式 策略模式( Strategy) 在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。 在策略模式中,我們創建表示各

設計模式—— 觀察者模式

pda 發出 dex obs index observe target 獲取 委托事件 觀察者模式類似出版社與訂閱者的關系,訂閱者能夠收到出版社發出的消息,可以取消訂閱。出版社在觀察者模式中稱為主題(Subject)訂閱者稱為觀察者(Observer)。 主

設計模式:裝飾模式

實體 裝飾者 java string rgs 大氣 sta 接口 rri 2018國慶黃金周來了,恰值國慶黃金周節假日,我想高速上一定車山車海,還好我選擇了高鐵,不然肯定需要尋找項目合作人或在高速上吃創業人士的炒飯炒面了。 國慶7天長假,天氣又如此的好,所謂風和日麗,如此良

Java的設計模式

子類 外觀模式 設計原則 有一個 單一職責原則 工廠 類適配器 總結 中文 oo設計原則 最少知識原則,只和朋友交談。該原則要求減少對象之間的交互。具體指的是只調用本對象中的成員變量的方法、當作參數傳進來的對象的方法,和本方法中創建的對象的方法。 總結來說,就是對方法的調

Java設計模式之建立型模式:抽象工廠模式

例子背景: 隨著客戶的要求越來越高,寶馬車需要不同配置的空調和發動機等配件。於是這個工廠開始生產空調和發動機,用來組裝汽車。這時候工廠有兩個系列的產品:空調和發動機。寶馬320系列配置A型號空調和A型號發動機,寶馬230系列配置B型號空調和B型號發動機。   一、概念:

常用的軟體設計模式簡單工廠模式

剛參加工作的時候,絲毫沒有面向物件的思想,所有分支都使用一堆的if...else...,現在回頭看以前的程式碼,著實low。但是隻要一心想著進步的人,一定是會總結和反省的,為什麼以前的程式碼出現bug這麼難維護,功能擴充套件那麼難,程式碼難得到複用,過了段時間連自己寫的程式碼都不知道什麼意思。都說女