委托和设计模式(2)(上)

2016-3-28 | 发布者:DotNet

(点击上方蓝字,可快速关注我们)


来源:懒得安分

链接:http://www.cnblogs.com/landeanfen/p/4710174.html


前言:这篇打算从设计模式的角度去解析下委托的使用。我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性。上次说过,方法可以理解为委托的实例,站在方法的层面,委托实例的一个非常有用的特性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的参数和返回值的兼容性。即只要方法的返回类型和参数表是相同的,则方法与委托类型兼容,方法的名称及方法所属类等信息委托是不关心的。有一定编程经验的大侠们肯定都接触过设计模式,其实设计模式大多数都是面向对象多态特性的体现,通过重写子类方法去展现不同的设计需求,这样看,既然是方法重写,那么方法的参数类型和返回值类型肯定是一致的,这是不是和委托的实例十分相似,这样说来,我们通过多态去实现的设计模式是否可以用委托的形式去代替。博主觉得,为了更好的理解委托,可以从这方面着手试试。。。


此篇简单抽取了几个设计模式分别按照多态和委托的方式去实现,当然这里的重点并不是讲设计模式,而是为了使读者更好地理解委托。所以设计模式的很多细节,本篇可能会略过。


一、简单工厂模式:本篇就借助计算器的例子加以说明。


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.桔子 /桔子虽然营养丰富,但含有叶红素,吃得过多容易引起「叶红素皮肤病」、腹痛腹泻,甚至骨...
健康养生道 • 
手淫造成的射精过快怎么治疗?

手淫造成的射精过快怎么治疗?

网友:我今年20岁已经有五六年的手淫习惯,头几年几乎是每天一两次,后来慢慢发现射精越来越快,有时候几乎是一碰就射精了。现在我基本五六秒就射精。我好象性欲特别强,没有阳痿的表现,就是射精太快。有没有办...
男人补肾壮阳宝典 • 
来势汹汹!过敏性鼻炎+捂热综合症,最近宝贝要闯...

来势汹汹!过敏性鼻炎+捂热综合症,最近宝贝要闯...

编者按:HI,家有萌宝的亲们,想知道宝贝的生长秘密是什么吗?想知道宝贝的健康有哪些细节被你不经意忽略了吗?“萌宝健康课”是浙江在线健康网推出的针对儿童健康保健的特色栏目,我们将联合杭城各大医院儿科,...
浙江在线健康网 • 
因为年轻所以不急于考虑健康问题?那你就错了

因为年轻所以不急于考虑健康问题?那你就错了

富含营养的食物让我们随着年龄的增长也能保持健康。要想在慢慢变老的过程中一直都能保持健康,关键是从年轻时就要养成健康饮食和锻炼的习惯。在年纪尚轻时,我们不大会考虑变老的问题。但是随着年岁渐长,我们经...
苏珊健康讲堂 • 
你是苹果型还是梨型身材?这对患心脏病风险有影...

你是苹果型还是梨型身材?这对患心脏病风险有影...

欢迎转载,转载请注明来源!  你是苹果型还是梨型身材?在美国心脏病学会2016年科学大会上发表的一项研究表明,身体的哪个部分承担更多重量可以比体重和身体质量指数(BMI)更加准确地预测你是否会患上心脏病...
中国生物技术网 • 
加载更多