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

标签:c#,职责链模式
0
投稿

猜你喜欢

  • 浅谈Java编程中的内存泄露情况

    2022-09-11 06:59:07
  • 使用WebSocket实现即时通讯(一个群聊的聊天室)

    2023-11-29 03:00:46
  • Spring容器初始化及问题解决方案

    2023-09-14 08:19:32
  • java内存模型jvm虚拟机简要分析

    2022-09-08 09:29:34
  • Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 实现分库分表功能

    2023-08-28 16:52:09
  • Android Google AutoService框架使用详解

    2023-07-19 22:48:14
  • 剑指Offer之Java算法习题精讲二叉树与N叉树

    2023-04-22 00:20:42
  • IDEA编译乱码Build Output提示信息乱码

    2023-08-07 12:14:35
  • 一文带你搞懂Java中方法重写与方法重载的区别

    2022-05-14 03:19:13
  • c#使用正则表达式匹配字符串验证URL示例

    2023-01-01 10:40:10
  • Java遍历json字符串取值的实例

    2023-09-02 17:03:17
  • eclipse的git插件安装、配置与使用详解

    2021-07-23 10:04:47
  • Spring @Async无法实现异步的解决方案

    2021-10-22 13:32:46
  • Java实现中文算数验证码的实现示例(算数运算+-*/)

    2023-10-23 03:08:09
  • 基于javassist进行动态编程过程解析

    2021-12-03 07:23:19
  • 使用java采集京东商城行政区划数据示例

    2023-04-17 06:31:52
  • C#使用文件流读取文件的方法

    2022-06-27 20:17:12
  • Android studio 出现错误Run with --stacktrace option to get the stack trace. Run with --info or --debu

    2022-10-25 09:43:15
  • 运行java的class文件方法详解

    2021-07-29 03:53:48
  • spring data jpa分页查询示例代码

    2023-01-09 14:51:50
  • asp之家 软件编程 m.aspxhome.com