title author date CreateTime categories
C# 设计模式 责任链
lindexi
2019-09-02 12:57:37 +0800
2018-2-13 17:23:3 +0800
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 那么就会直接被跳过,不会往下面执行,所以如果把最后一句代码替换前面的,那么就会调用责任链的下一处理,但是没有告诉他已经处理了。所以在责任链,需要注意同步和异步的转换,如果实在需要,那么请参见我的博客,如何把异步转同步。

2019-9-2-C#-设计模式-责任链的更多相关文章

  1. iOS设计模式 - 责任链

    iOS设计模式 - 责任链 原理图 说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  2. java 设计模式 -- 责任链模式

    设计模式 – 责任链模式 介绍: 责任链模式是一种动态行为模式,有多个对象,每一个对象分别拥有其下家的引用.连起来形成一条链.待处理对象则传到此链上,在此链进行传递,且待处理对象并不知道此会被链上的哪 ...

  3. 浅谈Python设计模式 -- 责任链模式

    声明:本系列文章主要参考<精通Python设计模式>一书,并且参考一些资料,结合自己的一些看法来总结而来. 之前在最开始就聊了Python设计模式有三种,其中关于创建型和结构型设计模式基本 ...

  4. 【设计模式】Java设计模式 - 责任链模式

    [设计模式]Java设计模式 - 责任链模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 目录 [设计模式]Java设计模式 - 责 ...

  5. 设计模式-责任链模式Chain of Responsibility)

    一.定义 职责链模式是一种对象的行为模式.在职责链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  6. 我的Java设计模式-责任链模式

    今天来说说程序员小猿和产品就关于需求发生的故事.前不久,小猿收到了产品的需求. 产品经理:小猿,为了迎合大众屌丝用户的口味,我们要放一张图,要露点的. 小猿:......露点?你大爷的,让身为正义与纯 ...

  7. Java设计模式-责任链模式

    提出问题: 最初接触责任链模式就是在struts2中,在当时学的时候看了一眼,大概知道了原理,最近在复习,模拟struts2,说是模拟只是大体模拟了struts2的工作流程,很多东西都是写死的,只是为 ...

  8. 设计模式——责任链(chain of responsibiltiy)

    责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象. 每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象.也就 ...

  9. Java与设计模式-责任链模式

    责任链模式属于行为型设计模式之中的一个,怎么理解责任链?责任链是能够理解成数个对象首尾连接而成,每个节点就是一个对象.每个对象相应不同的处理逻辑,直至有一个对象响应处理请求结束.这一种模式成为责任链模 ...

  10. php设计模式-责任链模式

    责任链模式更像是一种简化多种场景下调用处理的一种设计模式,特别适合if-else分支判断很多的场景.比如是根据不同会员等级给予不同的优惠力度. 它的定义:对象的调用是由下家的应用连接起来的处理链.一直 ...

随机推荐

  1. 记录--前端无感知刷新token & 超时自动退出

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前端无感知刷新token&超时自动退出 一.token的作用 因为http请求是无状态的,是一次性的,请求之间没有任何关系,服务端 ...

  2. 替代 Redis 的开源项目「GitHub 热点速览」

    近日,知名开源项目 Redis 宣布修改开源协议,从原来的「BSD 3-Clause 开源协议」改成「RSALv2 和 SSPLv1 双重许可证」.新的许可证主要是限制托管 Redis 产品的云服务商 ...

  3. 把分钟数转化成几小时几分钟(100 -> 01:40)

    /// <summary> /// 把分钟数转化成几小时几分钟(100 -> 01:40) /// </summary> /// <param name=" ...

  4. FPGA 原语之一位全加器

    FPGA原语之一位全加器 1.实验原理 一位全加器,三个输入,两个输出.进位输出Cout=AB+BC+CA,本位输出S=A异或B异或C.实验中采用三个与门.一个三输入或门(另外一个是两个或门,功能一致 ...

  5. exist和left join 性能对比

    今天遇到一个性能问题,再调优过程中发现耗时最久的计划是exist 部分涉及的三个表. 然后计划用left join 来替换exist,然后查询了很多资料,大部分都说exist和left join 性能 ...

  6. 前端 Typescript 入门

    前端 Typescript 入门 Ant design vue4.x 基于 vue3,示例默认是 TypeScript.比如 table 组件管理. vue3 官网介绍也使用了 TypeScript, ...

  7. #Dinic,最大权闭合子图#CF1473F Strange Set

    题目 分析 对于这种依赖关系,可以将正权值连源点,负权值连汇点, 然后 \(i\) 向 \(j(j<i)\) 连无穷大的边,注意到如果完全建图空间不够, 考虑记录每个约数最后一次出现的位置,直接 ...

  8. 组合数学——Min-Max容斥

    Min-Max 容斥,即 $$\max(S)=\sum_{T\in S,T\neq\emptyset}(-1)^{|T|-1}\min(T)$$ 接下来证明上面那个式子是对的.定义 \(S\) 中共有 ...

  9. 【FAQ】推送前台应用的通知处理功能没生效,如何进行排查?

    一.前台应用的通知处理简介 在调用推送接口时可以设置"foreground_show"字段控制前台应用的通知栏消息是否通过NC展示."foreground_show&qu ...

  10. 容器开发运维人员的 Linux 操作机配置优化建议

    "工欲善其事必先利其器", 作为一个PAAS平台架构师, 容器相关技术(docker, k8s等)是必不可少的. 本文简单介绍下我自己的Linux操作机配置. 提升工作效率, 提高 ...