第六讲 行为型设计模式

=========================================================================
 
《行为型设计模式》:关注对象和行为的分离
 
21.模板方法(Template Method)
 
【1】概念:在一个抽象类有固定的“算法骨架(就是操作步骤)”,而将一些步骤延迟到子类中去实现。
 
【2】好处:就是,我们可以在不破坏父类固定"算法"的前提下,重新定义一些特定的步骤,因为不变的步骤在模板里面,
                    变化的是在子类实现的,从而实现代码的服用和适配
 
【Q】:我们平时定义一个抽象类,里面可以有很多普通的方法,也可以有抽象方法,然后去继承实现,有什么不同呢?
 
A:我们定义普通的抽象方法和内容,仅仅是对行为的普通封装。而模板方法里面定义的各种行为,是某一个“算法”的实现
 
   骨架(步骤)。不是随便的封装方法!重要的是规范了方法调用的流程。
 
  
【3】应用举例:
namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("---------------------模板模式(微信)-----------------------");
            Temporder tm = new WeiXinPay();
            tm.TempStart();
            Console.WriteLine("---------------------模板模式(淘宝)-----------------------");
            tm =new TaoBaoPay();
            tm.TempStart();
            Console.Read();
        }
    }
}
 
 
namespace ConsoleApplication8
{
    /// <summary>
    /// 模板抽象类文件
    /// 适合有固定算法的,(就是说做什么步骤固定.但是步骤中有些操作不同)
    /// 比如这个案例,步骤是固定的只是有一些步骤的操作不一样,比如支付
    /// 面向对象:(提取不变的,封装变化的)
    /// </summary>
    public abstract class Temporder
    {
        /// <summary>
        /// 模板执行方法
        /// </summary>
        public void TempStart()
        {
            this.GetUserInfo();
            this.CreateOrder();
            this.Pay();
            this.Submit();
        }
        /// <summary>
        /// 第一步获取用户信息
        /// </summary>
        private void GetUserInfo()
        {
            Console.WriteLine("第一步获取用户信息");
        }
        /// <summary>
        /// 第二步创建订单
        /// </summary>
        private void CreateOrder()
        {
            Console.WriteLine("操作第二步创建订单");
        }
        /// <summary>
        /// 第三步支付,这个因为是可以调用不同的支付去掉,所以设置成虚方法,让子类实现,protected访问级别只能让继承自己的子类修改调用
        /// </summary>
        protected abstract void Pay();
        /// <summary>
        /// 第四部提交订单入库
        /// </summary>
        private void Submit()
        {
            Console.WriteLine("第四步提交订单入库");
        }
    }
}
 
namespace ConsoleApplication8
{
    public class TaoBaoPay:Temporder
    {
        /// <summary>
        /// 淘宝支付,实现具体的支付
        /// </summary>
        protected override void Pay()
        {
            Console.WriteLine("这个是使用淘宝支付");
        }
    }
}
 
namespace ConsoleApplication8
{
    public class WeiXinPay:Temporder
    {
        /// <summary>
        /// 实现具体的风支付,微信支付
        /// </summary>
        protected override void Pay()
        {
            Console.WriteLine("这个是微信的支付");
        }
    }
}
 
 
 
 
=========================================================================
 
“接口”的封装规范:在满足当前需要的前提下,让接口封装最大化(也就是不要用户使用的方法,数据,全部保护)
 
22.观察者模式(Observer)
 
【1】概念:观察者模式主要是说,有一个通知者,和它知道若干观察者,通知者发出一个通知,所有观察者接收通知。
 
         也就是一个通知者,在执行一个动作后,所监听它的所有观察者都能做出反应。
 
【2】特点:一个对象变化,需要通知其他的所有对象作出反映。但是不用关心多少个对象在关注它。
 
【3】典型应用:
 
【4】应用举例:
 
4.1 《双向依赖细节的实现》
 
         第一、创建通知者MonitorNotification
    
         第二、创建观察者StudentObserver
 
         第三、在控制台实现调用。
 
思考:以上设计存在的问题?
 
(1)通知者、观察者你中有我,我中有你,相互耦合!
 
(2)如果增加其他类型的观察者,比如有玩游戏的同学,有打电话的同学怎么办?修改麻烦!
 
(3)如果班长这个通知者有情况没来学校,无法通知,但是现在无法实现别人去代理!如果想实现,修改还是问题!
 
以上紧耦合的方式,是不能解决问题的!因为代码需要修改的地方很多!
 
思考解决方案?  按照依赖倒置原则,程序应该依赖抽象,而不是相互依赖细节!
 
具体方法:应该增加抽象观察者和抽象通知者!
 
namespace ConsoleApplication9.xijie
{
    /// <summary>
    /// 观察者(接收信息这)
    /// </summary>
    public class GuanCha
    {
        /// <summary>
        /// 观察者名称
        /// </summary>
        private string UserName { get; set; }
        private TongZhi Tz { get; set; }
        /// <summary>
        /// 初始化观察,并把观察者和通知者关联
        /// </summary>
        /// <param name="name"></param>
        /// <param name="m"></param>
        public GuanCha(string name,TongZhi m)
        {
            this.UserName = name;
            this.Tz = m;
        }
        /// <summary>
        /// 观察者接收信息的方法
        /// </summary>
        /// <param name="msg"></param>
        public void ReMsg()
        {
            Console.WriteLine(this.Tz.GName + "通知,观察者" + UserName + "你要赶快" + this.Tz.Msg);
        }
    }
}
 
 
 
namespace ConsoleApplication9.xijie
{
    /// <summary>
    /// 通知者,发送信息者
    /// 这种事原始的面向细节编程实现,强依赖
    /// </summary>
    public class TongZhi
    {
        /// <summary>
        /// 通知者名称
        /// </summary>
        public string GName { get; set; }
        /// <summary>
        /// 观察者列表,通职者要知道观察者都有哪些
        /// </summary>
        public List<GuanCha> Glist = new List<GuanCha>();
        /// <summary>
        /// 通知的信息
        /// </summary>
        public string Msg { get; set; }
        /// <summary>
        /// 添加观察者
        /// </summary>
        /// <param name="m"></param>
        public void AddG(GuanCha m)
        {
            this.Glist.Add(m);
        }
        /// <summary>
        /// 像观察者发送信息
        /// </summary>
        public void SendMsg()
        {
            foreach(var m in Glist)
            {
                m.ReMsg();
            }
        }
    }
}
 
            //面向细节编程,类双向关联
            Console.WriteLine("------------------------------面向细节编程-----------------------------------");
            TongZhi t = new TongZhi();
            t.Msg = "你要快点出发了";
            GuanCha g1 = new GuanCha("小王", t);
            GuanCha g2 = new GuanCha("小梨", t);
            GuanCha g3 = new GuanCha("小孙", t);
            GuanCha g4 = new GuanCha("小闫", t);
            t.AddG(g1);
            t.AddG(g2);
            t.AddG(g3);
            t.AddG(g4);
            t.SendMsg();
 
 
 
 
-------------------------------------------------------------------------------
 
4.2 《双向依赖抽象的实现》
 
     第一、创建抽象通知者AbstractNotification
    
     第二、创建抽象观察者AbstractObserver
 
     第三、创建具体通知者ConcreteNotification(可以有多个)
 
     第四、创建两个具体观察者ConcreteObserver (也可以有多个)
 
     第五、在控制台实现调用。(里氏替换原则)
 
进一步体会观察者模式的特点:
 
(1)通知者和观察者相互协作,轻松解耦!
 
(2)让耦合的双方依赖抽象,而不是依赖具体。也就是说自己的变化不会影响到其他。
 
(3)一个通知者,可以同时有多个观察者,一旦通知发布,所有的观察者都能收到,并且做出不同的“统一”反应。
 
继续思考问题:
 
实际应用中,观察者和通知者根本就没有任何关系!但是他们都需要接收通知者的通知,并做出反应!
 
这时候我们想,能不能把SendMsg()方法,让他根据灵活“多变”!
 
解决方法:使用委托或事件!通过委托只需要规定方法的原型,具体如何变化,只要在原型范围内均可。
namespace ConsoleApplication9.tty
{
    public abstract class GuanAbstrace
    {
        /// <summary>
        /// 观察者名称
        /// </summary>
        private string UserName { get; set; }
        private TAbstace Tz { get; set; }
        /// <summary>
        /// 初始化观察,并把观察者和通知者关联
        /// </summary>
        /// <param name="name"></param>
        /// <param name="m"></param>
        public GuanAbstrace(string name, TAbstace m)
        {
            this.UserName = name;
            this.Tz = m;
        }
        /// <summary>
        /// 观察者接收信息的方法
        /// </summary>
        /// <param name="msg"></param>
        public abstract void ReMsg();     
    }
}
 
 
 
 
 
namespace ConsoleApplication9.tty
{
   public abstract  class TAbstace
    {
        /// <summary>
        /// 通知者名称
        /// </summary>
        public string GName { get; set; }
        /// <summary>
        /// 观察者列表,通职者要知道观察者都有哪些
        /// </summary>
        public List<GuanAbstrace> Glist = new List<GuanAbstrace>();
        /// <summary>
        /// 通知的信息
        /// </summary>
        public string Msg { get; set; }
        /// <summary>
        /// 添加观察者
        /// </summary>
        /// <param name="m"></param>
        public abstract void AddG(GuanAbstrace m);
       
        /// <summary>
        /// 像观察者发送信息
        /// </summary>
        public abstract void SendMsg();    
    }
}
 
namespace ConsoleApplication9.tty
{
    /// <summary>
    /// 实现通职者
    /// </summary>
    public class TConrete:TAbstace
    {
        public override void AddG(GuanAbstrace m)
        {
            base.Glist.Add(m);
        }
        public override void SendMsg()
        {
            foreach(var m in base.Glist)
            {
                m.ReMsg();
            }
        }
    }
}
 
 
namespace ConsoleApplication9.tty
{
   public  class GConrete1:GuanAbstrace
    {
       public GConrete1(string name, TAbstace m) : base(name, m) { }
       /// <summary>
       /// 实现观察者
       /// </summary>
        public override void ReMsg()
        {
            Console.WriteLine("观察者1的信息");
        }
    }
}
 
 
namespace ConsoleApplication9.tty
{
    public class GConrete2:GuanAbstrace
    {
        public GConrete2(string name, TAbstace m) : base(name, m) { }
        /// <summary>
        /// 实现观察者
        /// </summary>
        public override void ReMsg()
        {
            Console.WriteLine("观察者2数据");
        }
    }
}
 
 
            //上面的代码是面向细节的,如果有业务变化很不方便,比如我们现在要给其他几种观察者推送信息怎么办,只能重新来一篇,
            //下面我们使用替换原则来做面向抽象的设计
            Console.WriteLine("------------------------------面向抽象编程-----------------------------------");
            TAbstace tzz = new TConrete();
            tzz.Msg = "开始上课了";
            GuanAbstrace gc1 = new GConrete1("小王", tzz);
            GuanAbstrace gc2 = new GConrete2("小李", tzz);
            tzz.AddG(gc1);
            tzz.AddG(gc2);
            tzz.SendMsg();
 
 
 
 
 
4.3 《委托动态依赖的实现》
 
     第一、创建通知者接口(和接口并列声明一个委托,用来规范“观察者”的响应动作)
    
     第二、实现通知者接口
 
     第三、创建不同的观察者(除了自己的任意行为外,再定义一个符合“委托”的方法!)
 
     第四、在客户端使用,并将“通知者对象的委托变量和观察者的委托实现方法关联”!
 
              客户端的通知者对象只需要调用委托变量即可调用所关联的全部观察者的方法!
 
这种方式的好处:彻底让观察者和通知者解耦,而再次联系的环节就是委托变量和委托方法实现。
 
namespace ConsoleApplication9.aaa
{
    /// <summary>
    /// 通职者接口
    /// </summary>
    public interface Tinterf
    {
        string Name { get; set; }
        string Message { get; set; }
        SendMsgdelegate DoSendMsg { get; set; }
        /// <summary>
        /// 发送通知
        /// </summary>
        /// <param name="msg"></param>
        void SendMsg(string msg);
    }
    /// <summary>
    /// 声明一个委托,规定观察者响应的方法的基础要求(原型)
    /// </summary>
    /// <param name="msg"></param>
    public delegate void SendMsgdelegate(string msg);
}
 
 
 
 
namespace ConsoleApplication9.aaa
{
    /// <summary>
    /// 实现通知接口
    /// </summary>
    public class TShiXian:Tinterf
    {
        public string Name { get; set; }
        public string Message { get; set; }
        public SendMsgdelegate DoSendMsg { get; set; }
        /// <summary>
        /// 实现发布通知的方法
        /// </summary>
        /// <param name="msg"></param>
        public void SendMsg(string msg)
        {
            DoSendMsg(msg);
        }
    }
}
 
 
namespace ConsoleApplication9.aaa
{
    /// <summary>
    /// 观察者1
    /// </summary>
    public class GConp1
    {
        public string Name { get; set; }
        //需要按照委托定义一个方法 public delegate void SendMsgDelegate(string msg);
        public void ResMsg(string msg)
        {
            Console.WriteLine("这个是A观察者的数据反馈"+msg);
            //实际开发中在这里编写具体的行为...
        }
    }
}
 
 
 
namespace ConsoleApplication9.aaa
{
    public class Gconp2
    {
        public string Name { get; set; }
        //需要按照委托定义一个方法 public delegate void SendMsgDelegate(string msg);
        public void ObserverAMethhod(string msg)
        {
            Console.WriteLine("观察者B的数据" + msg);
            //实际开发中在这里编写具体的行为...
        }
    }
}
 
 
            //上面这种还是有依赖关系,
            //下面我们弄一个,完全脱离依赖的
            Console.WriteLine("------------------------------面向委托编程-----------------------------------");
            Tinterf min = new TShiXian();
            GConp1 gp1 = new GConp1();
            Gconp2 gp2 = new Gconp2();
            min.DoSendMsg += gp1.ResMsg;
            min.DoSendMsg += gp2.ObserverAMethhod;
            min.SendMsg("速度了");
 
 
 
 
 
 
 
 
 
=========================================================================
 
23.责任链模式(Chain of Responsiblity)
 
【1】概念:当一个请求发出后,如果有多个对象需要处理请求,并且这种请求是有依次关系的,可以将多个对象组成一个
 
    “链条”,请求会沿着链条传递,直到有一个对象处理它为止。
 
【2】举例:公司请假审批,公司采购审批等各种审批手续的实现。
 
【3】应用举例:模拟公司请假流程的实现
 
【4】应用场景:
 
     第一,任务存在依次递进关系。
 
     第二,如果代码中有多个if else,并且还有可能扩展的,为降低耦合度,可以用本模式重构代码。
 
 
namespace ConsoleApplication10
{
    /// <summary>
    /// 请假请求
    /// </summary>
    public class LeavReq
    {
        public string Name { get; set; }
        public string Reason { get; set; }
        public int DayCount { get; set; }
    }
}
 
namespace ConsoleApplication10
{
    public abstract class AbstarHeand
    {
        /// <summary>
        /// 自关联
        /// </summary>
        protected AbstarHeand super = null;
        /// <summary>
        /// 类关系是聚合,外边初始化,是为了建立任务链
        /// </summary>
        /// <param name="m"></param>
        public void SetHandler(AbstarHeand m)
        {
            this.super = m;
        }
        /// <summary>
        /// 请假的处理
        /// </summary>
        /// <param name="l"></param>
        public abstract void ReqAction(LeavReq l);
    }
}
 
namespace ConsoleApplication10
{
    /// <summary>
    /// 实现审批虚拟类  (第一批级别审批者)
    /// </summary>
    public class Tabstar1:AbstarHeand
    {
        public override void ReqAction(LeavReq l)
        {
            if(l.DayCount>=1 && l.DayCount<=3)
            {
                Console.WriteLine("第一级审批,审批权限是3天");
            }
            else
            {
               if(this.super!=null)
               {
                   this.super.ReqAction(l);
               }
            }
        }
    }
}
 
namespace ConsoleApplication10
{
    /// <summary>
    /// 第二级审批者
    /// </summary>
    public class Tabstar2:AbstarHeand
    {
        public override void ReqAction(LeavReq l)
        {
            if(l.DayCount>3 && l.DayCount<=7)
            {
                Console.WriteLine("第二级别审批者,审批天数是7天");
            }
            else
            {
                if (this.super != null)
                {
                    this.super.ReqAction(l);
                }
            }
        }
    }
}
 
namespace ConsoleApplication10
{
    /// <summary>
    /// 第二级审批者
    /// </summary>
    public class Tabstar3 : AbstarHeand
    {
        public override void ReqAction(LeavReq l)
        {
            if(l.DayCount>7 && l.DayCount<=10)
            {
                Console.WriteLine("第三级别审批者,审批天数是7天");
            }
            else
            {
                if (this.super != null)
                {
                    this.super.ReqAction(l);
                }
            }
        }
    }
}
 
namespace ConsoleApplication10
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--------------------------任务链模式----------------------------------------");
            LeavReq ml = new LeavReq() { DayCount = 6, Name = "zzl", Reason = "想回家" };
            //这个是吧处理级别,做成一个链,,然后然请求逐个传递
            Tabstar1 my1 = new Tabstar1();
            Tabstar2 my2 = new Tabstar2();
            Tabstar3 my3 = new Tabstar3();
            my1.SetHandler(my2);
            my2.SetHandler(my3);
            //处理请假
            my1.ReqAction(ml);
            Console.Read();
        }
    }
}
posted @ 2019-04-08 18:08  瀚海行舟  阅读(165)  评论(0编辑  收藏  举报