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

前言:前篇 C#基礎系列——委託實現簡單設計模式 簡單介紹了下委託的定義及簡單用法。這篇打算從設計模式的角度去解析下委託的使用。咱們知道使用委託能夠實現對象行爲(方法)的動態綁定,從而提升設計的靈活性。上次說過,方法能夠理解爲委託的實例,站在方法的層面,委託實例的一個很是有用的特性是它既不知道,也不關心其封裝方法所屬類的詳細信息,對它來講最重要的是這些方法與該委託的參數和返回值的兼容性。即只要方法的返回類型和參數表是相同的,則方法與委託類型兼容,方法的名稱及方法所屬類等信息委託是不關心的。有必定編程經驗的大俠們確定都接觸過設計模式,其實設計模式大多數都是面向對象多態特性的體現,經過重寫子類方法去展示不一樣的設計需求,這樣看,既然是方法重寫,那麼方法的參數類型和返回值類型確定是一致的,這是否是和委託的實例十分類似,這樣說來,咱們經過多態去實現的設計模式是否能夠用委託的形式去代替。博主以爲,爲了更好的理解委託,能夠從這方面着手試試。。。html

  時間過得真快,轉眼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;
        }
    }

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

 

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

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,而不用在乎這四個方法在哪一個類下面,只要這四個方法的的參數和返回值和委託的定義保持一致便可。這也驗證了上面說的 「站在方法的層面,委託實例的一個很是有用的特性是它既不知道,也不關心其封裝方法所屬類的詳細信息,對它來講最重要的是這些方法與該委託的參數和返回值的兼容性」 。兩種方式獲得的結果是相同的:優化

 

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

一、純多態方式實現觀察者模式:這種代碼園子裏面很是多。spa

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();
     }
}

 

二、多態和委託實現觀察者模式。

   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方法的時候代碼的改變量要大。

 

3、模板方法模式,這裏就以設備採集爲例來進行說明:

一、多態實現模板方法模式:

    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是須要子類重寫的方法。模板方法模式在抽象類中定義了算法的實現步驟,將這些步驟的實現延遲到具體子類中去實現,從而使全部子類複用了父類的代碼,因此模板方法模式是基於繼承的一種實現代碼複用的技術。

 

二、使用委託改寫後:

    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來傳遞,方法的數量沒有什麼嚴格的限制。

 

固然其餘設計模式也可使用委託去優化設計,博主在這裏就暫時只分享這三種模式的異同。總的來講,委託不可能代替多態去實現各類模式,可是它和多態聯合起來使用能夠實現更加靈活的設計。經過這兩篇下來,不知道你是否對委託有點感受了呢,委託這東西,重在實戰,就像游泳同樣,若是不用那麼幾回,你永遠也不可能學會。以上只是博主我的的理解,可能不少方便沒有考慮得那麼全面,但願各位園友拍磚斧正~~

相關文章
相關標籤/搜索