聊聊设计模式

前言

为什么选择命令、装饰器、责任链模式来聊,因为自己对这三个涉及模式的实现与理解有些吃力,最近思考再三有所领悟,记录分享一下。但是学习过程中牵扯出其他几个模式的,哎,蜀道难。也希望可以帮助到对这三个模式有所迷惑的小朋友,或者有哪些更好的理解与可以提出或纠正我自身理解的错误。总之是一个相互学习点的抛出
本文类图及文末的连连看均取自《Head First设计模式》

命令模式

聊聊设计模式_第1张图片

有些不太好理解,结合一下案例就好理解了。dubbo中的telnet命令就是一个很好的案例,服务器端接口客户端请求处理的HeaderExchangeHandler节点中判断如果消息是字符串类型,则委派给TelnetHandler执行,实际有适配类型执行:TelnetHandlerAdapter,该适配器会根据命令行字符串解析出对应的命令来支持执行对应的命令。命令是通过dubbo自定义的spi加载的,所以如果想要新增或调整一个命令是不需要代码修改的。只需要按照spi规则增加对应的命令实现类以及kv配置即可。我们来根据这个案例套用至上面的类图。假定网络通信使用默认的netty
客户端-Client:NettyClient,就是实际的dubbo远程客户端
接受者-Receiver:NettyServer,服务端,其实也可以理解是TelnetHandlerAdapter担任了接收者角色
具体的命令-ConcreteCommand:ExtensionLoader,请求与执行者的k-v映射
命令-Command:TelnetHandler,命令接口
执行者-Invoker:InvokeTelnetHandler,及以下各种已实现的命令

clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler
exit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler
help=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler
status=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
log=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandler
ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler

适配器模式

组合方式,被适配者作为适配器的成员变量注入
聊聊设计模式_第2张图片

实现方式,同时实现两个接口
聊聊设计模式_第3张图片

其实简单的理解适配器就是实现了两个功能的接口。命令模式中举例的dubbo命令中就用到了该设计模式。Telnet命令与通道句柄适配器TelnetHandlerAdapter。该适配器接收客户端的请求handle连接、断开连接、发送消息、接收消息、处理异常。并将接收消息的received动作在适当的场景下(请求消息体为字符串时)将请求适配至TelnetHandler.telnet动作。执行对应的命令。而对应服务端认为TelnetHandler就是ChannelHandler句柄,是没有任何感知的,两个完全不同的类型被适配在一起,傻傻分不清楚

责任链模式

聊聊设计模式_第4张图片

dubbo的handle链就是责任链设计模式的一个案例,优缺点截图也很明确,尤其是我们实际业务系统中过滤链也是采用的该模式,缺点的暴露让我们一直很头疼。当一个业务问题抛出时,我们需要去定位该问题可能是走了哪些过滤链,问题是由该过滤链中哪个节点导致的?排查起来会略头疼,尤其是过滤链越来越多。hin难受
关于实现的两个细节

  1. 集合方式
  2. 装饰器方式

dubbo选择的是第二种。而我们实际的业务中使用的是第一种。区别的话比较明显了,一个空间换时间,一个时间换空间,当然因为时间、空间占用很小,几乎可以忽略,采用哪一种方式实现,个人认为,莫得区别。但是对于装饰器模式有两个风险点:

  1. 闭环
  2. 栈溢出

装饰器模式(包装器模式)

聊聊设计模式_第5张图片

例如dubbo服务端的责任链的默认装饰器如下图:
聊聊设计模式_第6张图片

模板方法模式

聊聊设计模式_第7张图片

该模式的案例想必每个人都会知道,spring框架的应用上下文,而且大家应该也曾感受到它钩子之多的恐惧吧,钩子套钩子,各种前后置动作,但这并不能阻止它的优秀。动态的扩展,几乎对老的业务功能无丝毫影响,如果硬要说有那就是增加一行钩子的影响,就能将新的功能点挂载到老服务上面

策略模式

聊聊设计模式_第8张图片

与模板方法类似,但是模板方法是面向继承,以及钩子方法来实现扩展。策略模式则面向接口,以组合的方式来实现扩展。我们实际业务中针对不同的监控有不同告警策略以及告警模板,下面是我们实际设计的类图。

  1. Analyze接口分析采集监控数据
  2. AlarmStrategy接口通过采集的数据判断是否需要告警,如果需要则根据数据组装返回告警模板
  3. AnalyzeTask异步执行告警
  4. AlarmService提供各种告警渠道:邮件、钉钉、短信、电话等

聊聊设计模式_第9张图片

总结

还有很多优秀的设计模式可以在各种实际场景中看到,学习,例如:slf4j的桥接模式,spring各个阶段事件ApplicationEvent发布功能使用的观察者模式,LinkedBlockingQueue的生产者与消费者模式等等。万变不离其宗,所有的设计模式都是围绕着开-闭原则
开放扩展
关闭修改
连连看走一波
聊聊设计模式_第10张图片
聊聊设计模式_第11张图片

你可能感兴趣的:(架构设计)