C#基础系列——委托和设计模式(二)

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

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

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来传递,方法的数量没有什么严格的限制。

当然其他设计模式也可以使用委托去优化设计,博主在这里就暂时只分享这三种模式的异同。总的来说,委托不可能代替多态去实现各种模式,但是它和多态联合起来使用可以实现更加灵活的设计。通过这两篇下来,不知道你是否对委托有点感觉了呢,委托这东西,重在实战,就像游泳一样,如果不用那么几次,你永远也不可能学会。以上只是博主个人的理解,可能很多方便没有考虑得那么全面,希望各位园友拍砖斧正~~

时间: 2024-12-06 17:48:41

C#基础系列——委托和设计模式(二)的相关文章

C#基础系列——委托实现简单设计模式

前言:上一篇介绍了下多线程的相关知识:C#基础系列--多线程的常见用法详解,里面就提到了委托变量.这篇简单介绍下委托的使用.当然啦,园子里面很多介绍委托的文章都会说道:委托和事件的概念就像一道坎,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里发慌.确实这东西就像最开始学C语言的指针一样,令人有一种很纠结的感觉,总觉得要调用一个方法直接调用就行了,为啥非要定义一个委托时执行这个方法呢.其实在C#里面很多的技术都是为了重用和简化代码而生,委托也不例外,很多使用C#多态去

Flutter基础系列之混合开发(二)

1.混合开发的场景 1.1作为独立页面加入 这是以页面级作为独立的模块加入,而不是页面的某个元素. 原生页面可以打开Flutter页面 Flutter页面可以打开原生页面 1.2作为页面的一部分嵌入 比如说原生页面中只有某一个item是Flutter: Flutter页面中只有某一部分是原生视图 2.Flutter混合开发的集成步骤 2.1创建Flutter Module 在做混合开发之前,我们首先需要创建一个Flutter Module. 这里建议Flutter Module的创建目录和原生工

C#基础系列——再也不用担心面试官问我“事件”了

前言:作为.Net攻城狮,你面试过程中是否遇到过这样的问题呢:什么是事件?事件和委托的区别?既然事件作为一种特殊的委托,那么它的优势如何体现?诸如此类...你是否也曾经被问到过?你又是否都答出来了呢?上两篇由浅及深介绍了下委托的用法,这篇还是来说说事件.希望通过这篇的介绍,博友能有个系统的认识,至少应付面试没问题了吧.不信?瞧瞧去~~ C#基础系列目录: C#基础系列——Linq to Xml读写xml C#基础系列——扩展方法的使用 C#基础系列——序列化效率比拼 C#基础系列——反射笔记 C

C#基础系列——一场风花雪月的邂逅:接口和抽象类

前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗.博主呵呵一笑,回想当初的自己,不也有此种疑惑么...今天打算针对他的问题,结合一个实际的使用场景来说明下抽象类和接口的异同,到底哪些情况需要用接口?又有哪些情况需要用抽象类呢? C#基础系列目录: C#基础系列——Linq to Xml读写xml C#基础系列——扩展方法的使用 C#基础系列——序

C#基础系列——异步编程初探:async和await

前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便.异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现. C#基础系列目录: C#基础系列——Linq to Xml读写xml C#基础系列——扩展方法的使用 C#基础系列——序列化效率比拼 C#基础系列——反射笔记 C#基础系列——At

C++重点知识点(基础系列二)

C++重点知识点基类 C++重点知识点(基础系列二),布布扣,bubuko.com

C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)

反射以及Attribute在ORM中的应用 一. 反射什么是反射?简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等.反射有什么用呢?反射不但让你在运行是获取对象的信息,还提供运行时动态调用对象方法以及动态设置.获取属性等的能力.反射在ORM中有什么用呢?我这里所讨论的ORM实现是通过自定义Attribute的方式进行映射规则的描述的.但是我们并不知道具体哪个对象需要对应哪个表,并且这些对象是独立于我们的ORM框架的,所以我们只能通过自定义Attr

[.net 面向对象编程基础] (21) 委托

[.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1.什么是委托? 学习委托,我想说,学会了就感觉简单的不能再简单了,没学过或都不愿了解的人,看着就头大,其实很简单.委托在.net面向对象编程和学习设计模式中非常重要,是学习.net面向对象编程必须要学会并掌握的. 委托从字面上理解,就是把做一些事情交给别人来帮忙完成.在C#中也可以这样理解,委托就是动态调用方法.这样说明,就很好理解了. 平时我们会

SQL Server调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行)

原文:SQL Server调优系列玩转篇二(如何利用汇聚联合提示(Hint)引导语句运行) 前言 上一篇我们分析了查询Hint的用法,作为调优系列的最后一个玩转模块的第一篇.有兴趣的可以点击查看:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 本篇继续玩转模块的内容,同样,还是希望扎实掌握前面一系列的内容,才进入本模块的内容分析. 闲言少叙,进入本篇的内容. 技术准备 数据库版本为SQL Server2012,利用微软的以前的案例库(Northwind)进行分析,