二十三种设计模式[13] - 职责链模式(Chain Of Responsibility)
前言
职责链模式,属于对象行为型模式。你可以想象成有一排人等着帮你处理问题,你只需要将你的问题告诉第一个人然后等待结果即可。如果第一个人处理不了这个问题,他会将这个问题告诉第二个人,以此类推。但并不意味着你的问题一定会被解决。
“ 它的目的是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。” ——《设计模式 - 可复用的面向对象软件》
结构
- Handler(公共接口):处理请求的接口,用来保证各个实现类的一致性;
- ConcreteHaandler(实现类):用来处理它负责的请求,如果不能处理则转发请求;
场景
考虑一个日志工具,它能够处理Info、Debug、Warn和Error4种日志类型。根据每种日志类型分别创建对应的类。当用户需要记录日志时,根据需要调用不同类型的日志类。
这种方式使得Client与每个日志类耦合,当发生日志记录业务时需根据需要使用不同的日志类。而在增加新的日志类型时,往往需要对Client做出较大的改动。现在按照职责链模式设计这个日志工具,如下。
在职责链中,Client只与其中一个日志类耦合,在修改链中的各个节点以及节点的顺序时不必对Client做出任何改动。对于Client来说,当发生日志记录业务时,只需要把请求交给一个日志类即可(但并不意味着当前请求一定会被处理)。
示例
public interface ILoggerHandler { ILoggerHandler Successor { set; get; } void Record(string message, LogLevelEmun logLevel); } public class InfoLogger : ILoggerHandler { public ILoggerHandler Successor { get; set; } = null; public void Record(string message, LogLevelEmun logLevel) { if(logLevel == LogLevelEmun.INFO) { Console.WriteLine(this.GetType().Name); Console.WriteLine($"记录日志[INFO]:{message}"); Console.WriteLine("---------------------------"); } else if (this.Successor != null) { this.Successor.Record(message, logLevel); } else { Console.WriteLine(this.GetType().Name); Console.WriteLine($"日志记录失败,不识别的日志类型“{logLevel}”"); Console.WriteLine("---------------------------"); } } } public class DebugLogger : ILoggerHandler { public ILoggerHandler Successor { get; set; } = null; public void Record(string message, LogLevelEmun logLevel) { if (logLevel == LogLevelEmun.DEBUG) { Console.WriteLine(this.GetType().Name); Console.WriteLine($"记录日志[DEBUG]:{message}"); Console.WriteLine("---------------------------"); } else if (this.Successor != null) { this.Successor.Record(message, logLevel); } else { Console.WriteLine(this.GetType().Name); Console.WriteLine($"日志记录失败,不识别的日志类型“{logLevel}”"); Console.WriteLine("---------------------------"); } } } public class WarnLogger : ILoggerHandler { public ILoggerHandler Successor { get; set; } = null; public void Record(string message, LogLevelEmun logLevel) { if (logLevel == LogLevelEmun.WARN) { Console.WriteLine(this.GetType().Name); Console.WriteLine($"记录日志[ERROR]:{message}"); Console.WriteLine("---------------------------"); } else if (this.Successor != null) { this.Successor.Record(message, logLevel); } else { Console.WriteLine(this.GetType().Name); Console.WriteLine($"日志记录失败,不识别的日志类型“{logLevel}”"); Console.WriteLine("---------------------------"); } } } public class ErrorLogger : ILoggerHandler { public ILoggerHandler Successor { get; set; } = null; public void Record(string message, LogLevelEmun logLevel) { if (logLevel == LogLevelEmun.ERROR) { Console.WriteLine(this.GetType().Name); Console.WriteLine($"记录日志[ERROR]:{message}"); Console.WriteLine("---------------------------"); } else if (this.Successor != null) { this.Successor.Record(message, logLevel); } else { Console.WriteLine(this.GetType().Name); Console.WriteLine($"日志记录失败,不识别的日志类型“{logLevel}”"); Console.WriteLine("---------------------------"); } } } public enum LogLevelEmun { INFO = 0, DEBUG = 1, WARN = 2, ERROR = 3 } static void Main(string[] args) { ILoggerHandler infoLogger = new InfoLogger(); ILoggerHandler debugLogger = new DebugLogger(); ILoggerHandler warnLogger = new WarnLogger(); ILoggerHandler errorLogger = new ErrorLogger(); infoLogger.Successor = debugLogger; debugLogger.Successor = errorLogger; //errorLogger.Successor = warnLogger; infoLogger.Record("AAAAAAAAAAAA", LogLevelEmun.INFO); infoLogger.Record("BBBBBBBBBBBB", LogLevelEmun.DEBUG); infoLogger.Record("CCCCCCCCCCCC", LogLevelEmun.WARN); infoLogger.Record("DDDDDDDDDDDD", LogLevelEmun.ERROR); Console.ReadKey(); }
职责链的好处在于可以随意改变内部的转发规则,并且用户可以从链中的任意节点发起请求。对于用户来说并不需要知道每个日志类型的处理方式,只需要将请求传递给其中一个节点即可。
总结
在职责链模式中,能够使请求的发起者与接收者解耦,并且其链状结构对发起者透明。也就是说用户不需要知道具体是哪个类处理的这个问题,只需要发送请求即可。虽然在链状结构中我们可以很轻易的对它的顺序及数量做出修改而不会影响发起者,但也使得每个请求会被多次转发从而影响执行效率。
以上,就是我对职责链模式的理解,希望对你有所帮助。
示例源码:https://gitee.com/wxingChen/DesignPatternsPractice
系列汇总:https://www.cnblogs.com/wxingchen/p/10031592.html
本文著作权归本人所有,如需转载请标明本文链接(https://www.cnblogs.com/wxingchen/p/10078636.html)