C#设计模式之职责链模式示例详解
作者:yangyang 发布时间:2023-11-08 05:26:38
前言
在软件开发中,我们通常会遇到一种场景,比如某个请求,会依次经过系统中的很多个模块来处理,如果某个模块处理不了,则将请求传递给下一个模块,比如在订单处理中,首先要经过用户校验,商品库存充足校验,如果不满足条件,返回错误,如果满足条件才会到下一步处理。
在ASP.NET Core里有middleware中间键的概念,每一个请求进来,都会经过一系列的Handler,这是一种职责链模式,每一个Handler都会决定是否处理该请求,以及是否决定将该请求传递给一下请求继续处理。
在.NET的委托中,也有一个委托链概念,当多个对象注册同一事件时,对象将委托放在一个链上,依次处理。
在JavaScript或者WPF的事件模型中,事件有冒泡和下沉,事件能够逐个向上级或者下级对象传递,每个对象都会决定是否会对该事件进行回应,或者终止事件的继续传递。
这些都是典型的职责链模式,责任链模式为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,沿着这条链传递请求,直到有对象处理它为止。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
示例1
假设在一个电脑游戏中,每个角色都有两个属性:攻击值和防御值。
public class Creature
{
public string Name;
public int Attack, Defense;
public Creature(string name, int attack, int defense)
{
Name = name;
Attack = attack;
Defense = defense;
}
public override string ToString()
{
return $"Name:{Name} Attack:{Attack} Defense:{Defense}";
}
}
现在这个角色会在游戏中进行活动,他可能会捡到一些武器增加自己的攻击值或者防御值。我们通过CreatureModifer来修改该对象的攻击值或者防御值。通常,在游戏中会有多个修改器会对同一角色进行修改,比如捡到武器A,然后捡到武器B等等,因此我们可以将这些修改器按照顺序附加到Creature对象上进行逐个修改。
在经典的职责链实现模式中,可以向下面这种方式来定义CreatureModifier对象:
public class CreatureModifier
{
protected Creature creature;
protected CreatureModifier next;
public CreatureModifier(Creature creature)
{
this.creature = creature;
}
public void Add(CreatureModifier m)
{
if (next != null)
{
next.Add(m);
}
else
{
next = m;
}
}
public virtual void Handle()
{
next?.Handle();
}
}
在这个类中:
构造函数里保存对待修改对象Creature的引用。
该类没有做多少工作,但他不是抽象类,其他类可以继承该对象。
Add方法可以添加其他CreatureModifier类到处理链条上。如果当前修改对象next对象为空,则赋值,否则将他添加到处理链的末端。
Handle方法简单调用下个处理链上对象的Handle方法。子类可以重写该方法以实现具体的操作。
现在,可以定义一个具体的修改类了,这个类可以将角色的攻击值翻倍。
public class DoubleAttackModifier : CreatureModifier
{
public DoubleAttackModifier(Creature c) : base(c)
{
}
public override void Handle()
{
creature.Attack *= 2;
Console.WriteLine($"Doubling {creature.Name}'s attack,Attack:{creature.Attack},Defense:{creature.Defense}");
base.Handle();
}
}
该类继承自CreatureModifier类,并重写了Handle方法,在方法里做了两件事,一是将Attack属性翻倍,另外一个非常重要,就是调用了基类的Handle方法,让职责链上的修改器继续进行下去。千万不要忘记调用,否则链条在这里就会终止了,不会继续往下传递了。
接着,新建一个增加防御值的修改器,如果攻击值小于2,则防御值增加1:
public class IncreaseDefenseModifier : CreatureModifier
{
public IncreaseDefenseModifier(Creature creature) : base(creature)
{
}
public override void Handle()
{
if (creature.Attack <= 2)
{
Console.WriteLine($"Increasing {creature.Name}'s defense");
creature.Defense++;
}
base.Handle();
}
}
现在整个应用代码如下:
Creature creature = new Creature("yy", 1, 1);
Console.WriteLine(creature);
CreatureModifier modi = new CreatureModifier(creature);
modi.Add(new DoubleAttackModifier(creature));//attack 2,defense 1
modi.Add(new DoubleAttackModifier(creature));//attack 4,defense 1
modi.Add(new IncreaseDefenseModifier(creature));//attack 4,defense 1
modi.Handle();
可以看到,第三个IncreaseDefenseModifier因为不满足attack小于等于2的条件,所以Defense没有修改。
示例2
下面这个例子来自 https://refactoring.guru/ ,首先定义一个包含用来建立处理链的方法,也定义一个处理请求的方法:
public interface IHandle
{
IHandle SetNext(IHandle handle);
object Handle(object request);
}
再定义一个抽象类,用来设置职责链和处理职责链上的请求。
public abstract class AbstractHandle : IHandle
{
private IHandle _nextHandle;
public IHandle SetNext(IHandle handle)
{
this._nextHandle = handle;
return handle;
}
public virtual object Handle(object request)
{
if (this._nextHandle != null)
{
return this._nextHandle.Handle(request);
}
else
{
return null;
}
}
}
在定义几个具体的职责链上处理的具体类:
public class MonkeyHandle : AbstractHandle
{
public override object Handle(object request)
{
if (request.ToString() == "Banana")
{
return $"Monkey: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}
public class SquirrelHandler : AbstractHandle
{
public override object Handle(object request)
{
if (request.ToString() == "Nut")
{
return $"Squirrel: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}
public class DogHandler : AbstractHandle
{
public override object Handle(object request)
{
if (request.ToString() == "MeatBall")
{
return $"Dog: I'll eat the {request.ToString()}.\n";
}
else
{
return base.Handle(request);
}
}
}
再定义使用方法,参数为单个职责链参数:
public static void ClientCode(AbstractHandler handler)
{
foreach (var food in new List<string> { "Nut", "Banana", "Cup of coffee" })
{
Console.WriteLine($"Client: Who wants a {food}?");
var result = handler.Handle(food);
if (result != null)
{
Console.Write($" {result}");
}
else
{
Console.WriteLine($" {food} was left untouched.");
}
}
}
现在定义流程处理链:
// The other part of the client code constructs the actual chain.
var monkey = new MonkeyHandler();
var squirrel = new SquirrelHandler();
var dog = new DogHandler();
monkey.SetNext(squirrel).SetNext(dog);
// The client should be able to send a request to any handler, not
// just the first one in the chain.
Console.WriteLine("Chain: Monkey > Squirrel > Dog\n");
ClientCode(monkey);
Console.WriteLine();
Console.WriteLine("Subchain: Squirrel > Dog\n");
ClientCode(squirrel);
输出结果为:
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.Subchain: Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.
总结
职责链模式是一个很简单的设计模式,在需要顺序处理请求比如命令或查询时,可以使用该模式。最简单的实现方式就是每个对象引用下一个待处理的对象,可以使用一个List或者LinkList来实现。
参考
https://refactoring.guru/design-patterns/chain-of-responsibility
https://stackoverflow.com/questions/48851112/is-the-chain-of-responsibility-used-in-the-net-framework
www.jb51.net/article/202503.htm
来源:https://www.yycoding.xyz/post/2020/12/19/introduction-to-design-patterns-of-chain-of-responsibility-pattern


猜你喜欢
- 一、Tomcat集成使用的成本越低,内部封装越复杂;1、依赖层级在SpringBoot框架的web依赖包中,引入的是内嵌Tomcat组件,基
- 有时我们可能会遇到下图这样一种情况 — 我们需要的资料或教程被分成了几部分存放在多个PDF文件中,不管是阅读还是保存都不是很方便,这时我们肯
- 1、抽象类 vs 接口 方法类型: 接口只能有抽象方法。抽象类可以有抽象和非抽象方法。从 Java 8 开始,它也可以有默认和静态
- 首先打开 Visual Studio Installer 可以看到vs2022 只支持安装4.6及以上的版本,如图所示。那么该如何安装4.6
- 本文实例讲述了C#实现顺序表(线性表)的方法。分享给大家供大家参考,具体如下:基本思想是使用数组作为盛放元素的容器,数组一开始的大小要实现确
- 本章,会对synchronized关键字进行介绍。涉及到的内容包括:1. synchronized原理2. synchronized基本规则
- 本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时
- Map在Java8中新增了两个replace的方法1.replace(k,v)在指定的键已经存在并且有与之相关的映射值时才会将指定的键映射到
- 需求有时候我们想快速通过http访问本地的一些资源,但是安装一些web服务器又很费时和浪费资源,而且也不是长期使用的。这时候我们可以启动一个
- Spring 中 Bean 的生命周期是当今最流行的 Java 开发框架之一,其强大的 Bean容器机制是其中的核心之一。Bean 是指在
- 为什么要给图片添加水印为图片添加水印的主要作用是保护图片版权,防止图片被未经授权的人使用或传播。通常情况下,图片水印会包含图片作者的名字、版
- 我们在构建C# Form窗口的时候经常需要到弹出新的窗口,那么接着就会如何弹出窗口的疑问。这里介绍最常见的两种弹窗方法show()和show
- 在网站开发中经常遇到级联数据的展示,比如选择城市的时候弹出的省市县选择界面。很多前端制作人员习惯于从JSON中而不是从数据库中获
- 首先,引入依赖:<dependency> <groupId>org.springframe
- C#之继承继承、封装和多态是面向对象编程的重要特性。其成员被继承的类叫基类也称父类,继承其成员的类叫派生类也称子类。派生类隐式获得基类的除构
- WPF中有一个DrawingContext类,该类提供了很多画法方法,例如DrawLine,DrawText,DrawRectangle等。
- 在C#中数组Array,ArrayList,泛型List都能够存储一组对象,但是在开发中根本不知道用哪个性能最高,下面我们慢慢分析分析。一、
- 简介有时候会需要在c#特别是WPF环境下调用其他的程序,这类型的程序以命令行为执行环境,这里就说明下如何调用exe并传递参数一般有两种方法一
- 本文实例为大家分享了ScrollView实现滚动效果的具体代码,供大家参考,具体内容如下如果长文本的内容超过一屏幕 则只能显示一屏幕的内容设
- 不记得从哪找的了,修改了部分代码,修复在Android平台下使用时,时区时间格式异常的问题。package cn.aikongmeng.de