设计模式那些模糊不清的概念

23种设计模式属于结构型模式,而mvc模式等属于架构型模式。本篇要讨论的设计模式指的是结构型设计模式。有时候我们在项目中使用设计模式,也不能准确的说出自己使用的是哪种设计模式。这就是设计模式难学的地方,总的来说,就是偏理论、难理解。

设计模式是前人总结出的一套高效编程方式,使用设计模式能够为功能模块提供更好扩展性,为代码带来更好的可读性,一定程度上也是对代码性能方面的调优。

当我们清楚的知道使用设计模式的目的在于此之后,也就不用再去生搬硬套的使用设计模式了。在实际开发中,我们需要结合实际场景考虑使用何种设计模式,即便使用同一种设计模式,在不同的场景下也有不同的用法。

23种设计模式中,每一种都可以稍作改动以迎合实际使用场景,并且这些设计模式不仅可以单独使用,有时候也可以组合使用,如组合模式与策略模式的组合使用。

责任链模式

参考维基百科:责任链模式包含了一些命令对象和一系列的处理对象,每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。

责任链模式我们并不陌生,Netty中的ChannelHandler、Sentinel的Slot都有运用责任链模式。Netty中的ChannelHandler责任链模式:

Sentinel中的Slot责任链模式:

举例Netty与Sentinel的目的也在于这两者在运用上存在些许区别,前者实现的是双向传递的责任链,后者则是单向传递。两者也存在共同点,都是使用链表将一系列处理对象连成责任链。

除了使用单向或者双向链表方式实现责任链调用模式,还可以使用集合实现,将处理对象按顺序放入集合中,通过遍历集合方式调用。

过滤器、拦截器与责任链的关系

在23种设计模式中,并没有对过滤器模式与拦截器模式单独定义,在wiki上也只是将这两者归类为结构型设计模式。

无论是过滤器模式还是拦截器模式,在实现上,两者都依赖责任链模式,因此,笔者将过滤器模式与拦截器模式看作是责任链模式的一种衍生模式,是责任链模式的一个变种,但不能使用等号。

过滤器模式是将所有过滤器对象构成链表,只要其中一个过滤器满足条件,那么后续的过滤器将不会被调用,命令最终可由该过滤器处理。

过滤器的过滤方法一般返回boolean值,表示过滤或不过滤,当过滤方法返回值类型为void时,表示过滤器在决定过滤当前命令后,必须对该命令作出响应以结束命令处理。

过滤器模式对过滤器的调用顺序并没有严格要求,是否要求顺序主要取决于具体的使用场景。

如需考虑优先级的黑白名单过滤器,顺序不同结果截然不同;

如Shiro使用过滤器模式实现的认证授权功能。

拦截器模式与过滤器模式大致相同,常见与动态代理模式结合使用,拦截器可以拦截方法调用(请求)或者改变方法调用(请求)传递的参数。

代理模式与委托模式的关系

参考维基百科:代理模式的目标是定义代理对象,该对象在客户端和实际主体之间操作,以控制对真实主体的访问,并在每次访问真实主题时执行必要的任务。

代理模式也分动态代理和静态代理,其中动态代理模式是使用最广、最多的代理模式。代理模式也是使用最多的设计模式之一。

参考维基百科:委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。代理模式与委托模式到底哪里不同也是饱受争议。

在23种设计模式中并没有对委托模式单独定义,有人认为,委托模式只是代理模式的别称,也有人认为,委托并不一定要调用相同方法,也不需要实现相同接口,而代理是代理一个方法或者一个接口的所有方法。

从定义上来看,两者确实是不同的设计模式,对于上述两种观点,如果要论对错,笔者更认可后者。

笔者曾经使用C#开发windows应用程序使用比较多的是委托模式,如处理一个日记事件,当监听到日记时,委托一个类去将日记显示到前端控件上,委托者委托受托者将日记显示,两者之间并没有严格的约定。

这是来自知乎的回答,可参考:委托,是委托人与受托人约定,由受托人处置委托人委托的事务的民事法律关系;代理是代理人在代理权限内,以被代理人的名义实施民事法律的行为。好像这样描述更难理解了。

组合模式与策略模式组合使用

参考维基百科:组合模式也叫复合模式,复合模式描述一组对象,这些对象与相同类型对象的单个实例处理的方式相同。复合的目的是将对象"组合"到树结构中,以表示部分整体层次结构。

通俗的理解,组合模式是将实现同一个接口的实例组合到一起,也可以将多个相同类型的组合组合到一起,构成一棵数。当外部调用这棵数的方法时,所有的叶子节点的方法都会被调用。

组合模式我们可能比较陌生,所以我们看一个实例来理解。

1、声明组合中的对象接口:组件(Component),并提供接口实现类(Leaf);

2、定义具有组件行为的组合类,存储组件、支持添加组件;

3、通过组合操作组合中的对象。

参考维基百科:策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

组合模式与策略模式组合使用,在SpringBoot的web框架中非常常见,例如实现方法参数解析。

定义方法参数解析器(HandlerMethodArgumentResolver),HandlerMethodArgumentResolver即是策略模式中的策略接口(Strategy),也是组合模式中的组件(Component)。

组合模式将具有相同处理方式的对象组合到一起,也就是将所有方法参数解析器组合到一起。

策略模式根据方法参数的类型或者参数上的注解等选择一个能够解析该方法参数的解析器,由解析器从请求(数据包)中解析出方法参数。

HandlerMethodArgumentResolverComposite即是组合模式中的组合(Composite),也是策略模式中的Context。

RequestResponseBodyMethodProcessor负责解析被@ResponseBody注解注释的方法参数、PathVariableMethodArgumentResolver负载解析被@PathVariable注解注释的方法参数,这些都是组合模式中的叶子节点(Leaf),也是策略模式中的实体策略。

适配器模式

适配器模式的定义:适配器模式是使因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。

以实现支付结果回调失败定时重试为例,假设现有两种失败定时重试实现方案:

一种是支持使用cron表达式的失败定时重试;

另一种是支持自定义周期的失败定时重试,如间隔2秒、4秒、8秒...后重试。

使用适配器模式将两种方式组合到一起对外提供服务,并且做一些扩展。

对于外部调用者而言,只需要访问一个接口就能获得多种方式的支持,内部多种方式一起对外提供服务。

适应不同场景的使用方式

如何理解设计模式应当为适应不同场景作出改变?我们以封装json序列化和反序列化工具为例。

在实际项目中,我们可以使用gson、Jackson等框架实现Java对象的序列化与反序列化,但一般我们不会同时使用多个,也不会存在面对不同的业务使用不同框架的情况。相反的,一般我们只会选择其中一种,并且不会经常改变。

使用策略模式可以实现后期切换框架而不需要修改业务代码,但这种场景下,我们真的需要教科书式的实现策略模式吗。

最适合的方法应当是根据引入不同的框架决定使用哪种序列化策略,通过类加载实现“策略”,这种微妙的改变为封装的组件提供了更智能的策略。

例如,我们在项目中添加gson的依赖后,整个项目的序列化和反序列化工作都将由gson完成,而当我们把依赖gson改为依赖Jackson时,整个项目的序列化和反序列化工作都将自动切换到Jackson。

设计模式不应该被生搬硬套的使用,否则也就违背了使用设计模式的初衷。如果你也赞同我的观点,欢迎点赞、在看、转发。


推荐阅读

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值