C# 设计模式 责任链 后退按钮使用责任链

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。《JAVA与模式》

我们在 C# 也可以使用责任链。

首先我们需要一个接口IHandle接受我们的责任,在里面,最简单的责任链只有 Successor 和 Request AddSuccessor ,请看代码

    public interface IHandle
    {
        IHandle Successor
        {
            set;
            get;
        }

        void Request(string str);

        void AddSuccessor(IHandle successor);
    }

责任链的下一个责任接受:Successor

处理责任:Request

添加处理责任的下一个:AddSuccessor

然后我们需要一个实际处理类,这个类集成接口IHandle。可以看到我的接口IHandle 只是处理字符串,其实我们可以处理很多的,但是为了简单,我们就先写字符串。

假如我们是员工,发起的请求是叫老板加工资,那么开始决定工资还不是他,需要经过主管、HR、然后是老板,他们组成了一个责任链。

首先我们定义员工,他可以发送出责任需要让我们的具体处理者处理,但是我们这时看到了主管等其实有重复的,如果主管不同意处理,那么就没必要进行HR同意,所以我们的IHandleAreHandle是不是被处理,因为无论是不是被处理还是要传给下一个,他需要决定这个处理我要不要继续,如果我们的员工提出要求,主管看他不爽,每次都不给他提工资,而我们HR也因为主管处理了,他就不接受,那么这样,员工就得不到提工资,为了解决,我们让HR也要接收到,也就是不管如何都把这个传到下一层,当然这样就需要传下一个AreHandle。如果觉得不需要,那么就不用传下。

另一个,我们不知道我们什么时候实现主管、什么时候实现HR,我们责任链的位置重要,因为如果主管处理了,HR就可以不处理,那么我们需要顺序,一般在添加那里加上权限,这里写Access。我们在添加具体处理,一般判断我们的下一个处理是否存在,如果不存在,直接添加输入参数下一个处理,如果存在,判断权限大小,如果比他大就代换他,如果比他小,就给下一个处理。

        public void AddSuccessor(IHandle successor)
        {
            if (Successor == null)
            {
                Successor = successor;
            }
            else
            {
                if (successor.Access > Successor.Access)
                {
                    var temp = Successor;
                    Successor = successor;
                    successor.Successor = temp;
                }
                else
                {
                    Successor.AddSuccessor(successor);
                }
            }
        }

我们发现如果把具体处理写为一个类,然后实现,这样每次使用函数Request都用成员,这样不太好,因为我们有功能扩展,所以我们把ConcreteHandler写为抽象,然后继承,写出主管等的类。

    public class GHandle : ConcreteHandler
    {
        public override void Request(string str)
        {
            NotifyProperty.Reminder?.Invoke("经理处理"+str);
            Successor?.Request(str);
        }
    }

    public class DeptHandle : ConcreteHandler
    {
        public override void Request(string str)
        {
            NotifyProperty.Reminder?.Invoke("主管处理"+str);
            Successor?.Request(str);           
        }
    }

后退按钮使用责任链

我看到堆栈炸了有人问我,为什么一按后退就炸。

我看了他的源代码,他每个页面都把后退按钮点击事件+=他的方法。

我们可以使用UWP的后退按钮,但是需要小心,在哪些处理需要知道,不可以在每个需要处理都添加事件。

那么如何添加后退按钮,才可以在需要后退的时候进行后退,可以用到上面说的设计,添加一个链,需要做一个类,如果直接写,看起来比较难。

新建一个类,这个类用做责任,通过这个类,可以做 MVVM ,如果对于这个不熟,请看 win10 uwp MVVM入门

本文告诉大家如何做出双击退出应用。

首先需要创建两个类作为责任链,请看下面。

  /// <summary>
    /// 责任链模式
    /// </summary>
    public class FjyhtrOcbhzjwi
    {
        public static FjyhtrOcbhzjwi Fhnazmoul { get; } = new FjyhtrOcbhzjwi();

        public bool Handle { get; set; }

        public void AddSuccessor(AjuvqrDqsoljna ajuvqrDqsoljna)
        {
            AjuvqrDqsoljna.AddSuccessor(ajuvqrDqsoljna);
        }

        public void RemoveSuccessor(AjuvqrDqsoljna ajuvqrDqsoljna)
        {
            AjuvqrDqsoljna.RemoveSuccessor(ajuvqrDqsoljna);
        }

        public void Request()
        {
            Handle = false;
            AjuvqrDqsoljna.Request(this);
        }

        private IHandle AjuvqrDqsoljna { set; get; } = new AjuvqrDqsoljna(fjyhtrOcbhzjwi => { });
    }

    public interface IHandle
    {
        IHandle Successor
        {
            set;
            get;
        }

        int Hnkdqckyr { get; }

        void Request(FjyhtrOcbhzjwi fjyhtrOcbhzjwi);

        void AddSuccessor(IHandle ajuvqrDqsoljna);
        void RemoveSuccessor(IHandle ajuvqrDqsoljna);
    }

    public class AjuvqrDqsoljna : IHandle
    {
        public AjuvqrDqsoljna(Action<FjyhtrOcbhzjwi> request)
        {
            Request = request;
        }

        /// <summary>
        /// 权限
        /// </summary>
        public int Hnkdqckyr { set; get; }

        public Action<FjyhtrOcbhzjwi> Request { get; }

        IHandle IHandle.Successor { get; set; }

        void IHandle.Request(FjyhtrOcbhzjwi fjyhtrOcbhzjwi)
        {
            Request.Invoke(fjyhtrOcbhzjwi);
            ((IHandle) this).Successor?.Request(fjyhtrOcbhzjwi);
        }

        void IHandle.AddSuccessor(IHandle successor)
        {
            if (((IHandle)this).Successor == null)
            {
                ((IHandle) this).Successor = successor;
            }
            else
            {
                if (successor.Hnkdqckyr > ((IHandle) this).Successor.Hnkdqckyr)
                {
                    var temp = ((IHandle) this).Successor;
                    ((IHandle) this).Successor = successor;
                    successor.Successor = temp;
                }
                else
                {
                    ((IHandle) this).Successor.AddSuccessor(successor);
                }
            }
        }

        void IHandle.RemoveSuccessor(IHandle ajuvqrDqsoljna)
        {
            if (ajuvqrDqsoljna == null)
            {
                return;
            }
            if (((IHandle) this).Successor == ajuvqrDqsoljna)
            {
                ((IHandle) this).Successor = ((IHandle) this).Successor.Successor;
            }
            else
            {
                ((IHandle) this).Successor?.RemoveSuccessor(ajuvqrDqsoljna);
            }
        }
    }

在使用的时候,通过调用FjyhtrOcbhzjwi就可以获得插入新的处理。接下来就是需要返回的按钮,参见win10 UWP 标题栏后退

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            //启动后退关闭
            Windows.UI.Core.SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.Visible;
            Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += BackRequested;
            FiontwzNdqd();
        }

可以看到FiontwzNdqd函数就是做双击退出的,但是双击退出需要一个字段记录是否退出。

        private bool _ajuvqrDqsoljnaMkfsjNiydfobt;

然后就是添加双击退出的函数

        private void FiontwzNdqd()
        {
            var ajuvqrDqsoljna = new AjuvqrDqsoljna(fjyhtrOcbhzjwi =>
            {
                //双击退出
                if (fjyhtrOcbhzjwi.Handle)
                {
                    _ajuvqrDqsoljnaMkfsjNiydfobt = false;
                    return;
                }
                if (_ajuvqrDqsoljnaMkfsjNiydfobt)
                {
                    Application.Current.Exit();
                }
                else
                {
                    LyfxkdxmSzjd.Text= "再次点击退出";
                    _ajuvqrDqsoljnaMkfsjNiydfobt = true;
                }
            });
            FjyhtrOcbhzjwi.Fhnazmoul.AddSuccessor(ajuvqrDqsoljna);
        }

但是这样写没有动画,于是添加动画,有关动画,参见:win10 UWP 动画

        private void ViewModel_LyfxkdxmSzjd(object sender, string e)
        {
            LyfxkdxmSzjd.Text = e;
            LyfxkdxmSzjd.Visibility = Visibility;
            LyfxkdxmSzjd.Opacity = 1;

            Storyboard sb = new Storyboard();
            DoubleAnimation animation = new DoubleAnimation
            {
                From = 1,
                To = 0,
                Duration = new Duration(TimeSpan.FromSeconds(2))
            };
            Storyboard.SetTargetName(animation, nameof(LyfxkdxmSzjd));
            Storyboard.SetTargetProperty(animation, "Opacity");
            sb.Children.Add(animation);
            sb.Completed += (o, ee) =>
            {
                LyfxkdxmSzjd.Visibility = Visibility.Collapsed;
                _ajuvqrDqsoljnaMkfsjNiydfobt = false;
            };
            Storyboard.SetTarget(animation, LyfxkdxmSzjd);
            sb.Begin();
        }

这里的动画这样写是因为在 ViewModel 有一个事件,这个事件就是通知,于是就需要添加事件,在界面显示。刚好在显示结束的时候关闭双击退出。

在我之前写的游戏win10 uwp 商业游戏进入游戏时,用户按下返回按钮,需要返回欢迎界面,那么这时候就需要添加后退的处理。

因为我添加的是 MVVM 框架,于是在跳转进游戏的 ViewModel 时添加处理。关于这个框架,请看win10 uwp MVVM 轻量框架这里,但是我不会在本文用了太多这个框架的东西。在没有看这篇文章还是可以继续阅读。

        public override void OnNavigatedTo(object sender, object obj)
        {
            //省略其他的代码

            _ajuvqrDqsoljna = _ajuvqrDqsoljna ?? new AjuvqrDqsoljna(async fjyhtrOcbhzjwi =>
            {
                if (fjyhtrOcbhzjwi.Handle)
                {
                    return;
                }
                fjyhtrOcbhzjwi.Handle = true;
              
                //游戏保存
                await AccountGoverment.JwAccountGoverment.Storage();
                //返回上一层
                Send(new BackTvvxwlwIlibbcpMessage(this));

                //fjyhtrOcbhzjwi.Handle = true; 不要写在后面

            }){
                   Hnkdqckyr = 10
              };
            FjyhtrOcbhzjwi.Fhnazmoul.AddSuccessor(_ajuvqrDqsoljna);
        }

上面代码主要是添加在后退时,保存游戏和返回到上一层,代码最重要的是使用fjyhtrOcbhzjwi.Handle = true,于是在他后面的处理就可以知道自己需要或不需要处理。

当然自己添加的处理也是需要判断当前是否已经有权限比他高的进行处理,如果有,就不处理。这样写就可以在游戏进行返回。

上面代码用到框架只有一句Send(new BackTvvxwlwIlibbcpMessage(this)) 他可以让页面返回上一页,只需要发送消息,不需要知道如何去做。

需要知道的是关于 async 可能出现一个问题,请看代码,最后我去掉了fjyhtrOcbhzjwi.Handle,说不要写在后面。这里的意思是如果调用一个方法,这个方法有 await 那么这个方法如果不是 async Task 那么就会直接被跳过,不会往下面执行,所以如果把最后一句代码替换前面的,那么就会调用责任链的下一处理,但是没有告诉他已经处理了。所以在责任链,需要注意同步和异步的转换,如果实在需要,那么请参见我的博客,如何把异步转同步。


本文会经常更新,请阅读原文: https://lindexi.gitee.io/lindexi/post/C-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%B4%A3%E4%BB%BB%E9%93%BE.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://lindexi.gitee.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

Web开发感悟:数据绑定是一种技术,更是一门艺术

1、前言 作为一个多年从事b/s开发的程序猿,曾先后使用过asp、asp.net做为主要服务端语言。不管是相对低级的asp也好,还是高级的asp.net也罢,都...

22750
来自专栏小巫技术博客

Android触摸事件机制

12930
来自专栏Web 开发

又是一年PC操作系统三国鼎立的时刻~

Win8 在今早零时,发布开发者预览版,Lion已经发布多时,Ubuntu11.10的正式版也已经逼近

9000
来自专栏小文博客

良心压缩软件Bandizip——无广告超精简

12.7K50
来自专栏葡萄城控件技术团队

ActiveReports 报表应用教程 (6)---分组报表

在葡萄城ActiveReports报表中可以设置单级分组、嵌套分组,同时,还可以使用表格、列表以及矩阵等数据区域控件对数据源进行分组操作。分组报表在商业报表系统...

25050
来自专栏转载gongluck的CSDN博客

SAPI SDK的介绍

我们都使用过一些某某词霸的英语学习工具软件,它们大多都有朗读的功能,其实这就是利用的Windows的TTS(Text To Speech)语音引擎。它包含在Wi...

56570
来自专栏生信技能树

爬虫写完了,运行了,然后呢?

看到这个问题,我首先想到的是R和python。基础的爬虫无非是:构建URL、根据页面结构解析爬取关键信息、整理数据格式输出结果。

30530
来自专栏转载gongluck的CSDN博客

波形音频(WAVE)底层接口的学习与使用

在WINDOWS下,音频函数有多种类型,如MCI、多媒体OLE控制、高级音频等,使用方法都比较简单。 但如果想编写一个功能较强大的音频处理程序,那...

1.4K50
来自专栏睿哥杂货铺

SDN 技术指南(四):Open vSwitch

由之前发布的文章知道 Open vSwitch(Open Source Virtual Switch) 是一款基于软件实现的开源虚拟交换机。

33160
来自专栏SAP最佳业务实践

SAP最佳业务实践:MM–组件收费的委外加工(251)-6开销售发票

4.7 创建出具发票凭证 创建出具发票凭证给委外加工商。 完成了对委外加工商的发货。 SAP ECC菜单Processes -Create Invoices f...

46880

扫码关注云+社区

领取腾讯云代金券