前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快速梳理常用的设计模式(中篇)

快速梳理常用的设计模式(中篇)

作者头像
Rude3Knife的公众号
发布2019-08-07 11:35:10
4020
发布2019-08-07 11:35:10
举报
文章被收录于专栏:后端技术漫谈后端技术漫谈

前言

本文旨在快速梳理常用的设计模式,了解每个模式主要针对的是哪些情况以及其基础特征,每个模式前都有列举出一个或多个可以深入阅读的参考网页,以供读者详细了解其实现。

分为三篇文章:

面试知识点复习手册

全复习手册文章导航

点击公众号下方:技术推文——面试冲刺

快速回忆

行为型

  • 责任链(Chain Of Responsibility)
  • 命令(Command)
  • 解释器(Interpreter)
  • 迭代器(Iterator)
  • 中介者(Mediator)
  • 备忘录(Memento)
  • 观察者(Observer)
  • 状态(State)
  • 策略(Strategy)
  • 模板方法(Template Method)
  • 访问者(Visitor)
  • 空对象(Null)

理念

首先搞清楚一点,设计模式不是高深技术,不是奇淫技巧。设计模式只是一种设计思想,针对不同的业务场景,用不同的方式去设计代码结构,其最最本质的目的是为了解耦,延伸一点的话,还有为了可扩展性和健壮性,但是这都是建立在解耦的基础之上。

高内聚低耦合

高内聚:系统中A、B两个模块进行交互,如果修改了A模块,不影响模块B的工作,那么认为A有足够的内聚。

低耦合:就是A模块与B模块存在依赖关系,那么当B发生改变时,A模块仍然可以正常工作,那么就认为A与B是低耦合的。

行为型

责任链(Chain Of Responsibility)

https://blog.csdn.net/maoyuanming0806/article/details/80183494

意图

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

职责和角色

  • Handler:处理类的抽象父类
  • concreteHandler:具体的处理类

设计使用责任链的基本流程

  • 组织对象链:将某人物的所有职责执行对象以链的形式加以组织起来
  • 消息或请求的传递:将消息或请求沿着对象链传递,让处于对象链中的对象得到处理机会
  • 对象链中对象的职责分配:不同对象完成不同职责
  • 任务的完成:对象链末尾的对象结束任务并停止消息或请求的继续传递。

应用实例

红楼梦中的"击鼓传花"。 JS 中的事件冒泡。 JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter,springMVC的拦截器

何时使用:在处理消息的时候以过滤很多道。

如何解决:拦截的类都实现统一接口。

关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

实现举例

在这里插入图片描述

处理器的抽象类

代码语言:javascript
复制
package com.mym.designmodel.CoRModel;

/**
 * 职责:Handler 职责类的抽象父类
 */
public abstract class AbstractCarHandler {

    AbstractCarHandler carHandler = null;

    public abstract void carHandler();

    public AbstractCarHandler setNextCarHandler(AbstractCarHandler nextCarHandler){
        this.carHandler = nextCarHandler;
        return this.carHandler;
    }

    /**职责下传*/
    protected void doChain(){
        if(this.carHandler != null){
            this.carHandler.carHandler();
        }
    }
}

责任链一个执行者1

代码语言:javascript
复制
package com.mym.designmodel.CoRModel;

/**
 * 职责:concreteHandler 具体的处理类
 */
public class CarHeadHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("处理车的head!");

        //下传
        this.doChain();
    }
}

责任链一个执行者2

代码语言:javascript
复制
package com.mym.designmodel.CoRModel;

/**
 * 职责:concreteHandler 具体的处理类
 */
public class CarBodyHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("处理车的body!");

        //下传
        this.doChain();
    }
}

责任链一个执行者3

代码语言:javascript
复制
package com.mym.designmodel.CoRModel;

/**
 * 职责:concreteHandler 具体的处理类
 */
public class CarTailHandler extends AbstractCarHandler {
    @Override
    public void carHandler() {
        System.out.println("处理车的tail!");

        //下传
        this.doChain();
    }
}

客户端client

代码语言:javascript
复制
package com.mym.designmodel.CoRModel;

/**
 * 测试
 */
public class MainClass {

    public static void main(String[] args) {
        AbstractCarHandler carheadHandle = new CarHeadHandler();
        AbstractCarHandler carbodyHandle = new CarBodyHandler();
        AbstractCarHandler carTailHandler = new CarTailHandler();

        //组装责任链
        carheadHandle.setNextCarHandler(carbodyHandle).setNextCarHandler(carTailHandler);

        //链头部开始执行
        carheadHandle.carHandler();
    }
}

JDK

  • java.util.logging.Logger#log()
  • Apache Commons Chain
  • javax.servlet.Filter#doFilter()

命令(Command)

https://www.cnblogs.com/konck/p/4199907.html

意图

命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。

解决了这种耦合的好处我认为主要有两点:

1.更方便的对命令进行扩展(注意:这不是主要的优势,后面会提到)

2.对多个命令的统一控制(这种控制包括但不限于:队列、撤销/恢复、记录日志等等)

应用实例

struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。

类图

  • Command:命令
  • Receiver:命令接收者,也就是命令真正的执行者
  • Invoker:通过它来调用命令
  • Client:可以设置命令与命令的接收者

JDK

  • java.lang.Runnable
  • Netflix Hystrix
  • javax.swing.Action

解释器(Interpreter)

https://www.cnblogs.com/chenssy/p/3346427.html

意图

所谓解释器模式就是定义语言的文法,并且建立一个解释器来解释该语言中的句子。

这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

应用实例

编译器、运算表达式计算。

JDK

  • java.util.Pattern
  • java.text.Normalizer
  • All subclasses of java.text.Format
  • javax.el.ELResolver

迭代器(Iterator)

https://www.jianshu.com/p/3d0406a01b73

意图

提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。

迭代器模式的优缺点

优点

①简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。

②可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。

③封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。

缺点

对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,大家可能都有感觉,像ArrayList,我们宁可愿意使用for循环和get方法来遍历集合。

JDK

  • java.util.Iterator
  • java.util.Enumeration

中介者(Mediator)

http://www.runoob.com/design-pattern/mediator-pattern.html

意图

集中相关对象之间复杂的沟通和控制方式。

实现

Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:

在这里插入图片描述

使用中介者模式可以将复杂的依赖结构变成星形结构:

在这里插入图片描述

JDK

  • All scheduleXXX() methods of java.util.Timer
  • java.util.concurrent.Executor#execute()
  • submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
  • scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
  • java.lang.reflect.Method#invoke()

备忘录(Memento)

https://blog.csdn.net/o279642707/article/details/60767258

意图

在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。

主要解决

所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

应用实例

1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 5、数据库的事务管理。

JDK

  • java.io.Serializable

观察者(Observer)

https://www.jianshu.com/p/fc4554cda68d

意图

观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

在这里插入图片描述

何时使用

一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

实现举例

天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。

在这里插入图片描述

JDK

  • java.util.Observer
  • java.util.EventListener
  • javax.servlet.http.HttpSessionBindingListener
  • RxJava

状态(State)

https://www.cnblogs.com/java-my-life/archive/2012/06/08/2538146.html

意图

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。

应用实例

考虑一个在线投票系统的应用,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票次数超过5次,则判定为恶意刷票,要取消该用户投票的资格,当然同时也要取消他所投的票;如果一个用户的投票次数超过8次,将进入黑名单,禁止再登录和使用系统。

策略(Strategy)

https://www.jianshu.com/p/7fa8ad000a97

https://www.cnblogs.com/wenjiang/p/3352041.html

意图

定义一系列算法,封装每个算法,并使它们可以互换。

策略模式和状态模式的区别:

之所以说状态模式是策略模式的孪生兄弟,是因为它们的UML图是一样的,但意图却完全不一样,**策略模式是让用户指定更换的策略算法,而状态模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态。 **

策略模式可以让算法独立于使用它的客户端。

具体场景实现

假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

根据描述,折扣是根据以下的几个算法中的一个进行的:

算法一:对初级会员没有折扣。

算法二:对中级会员提供10%的促销折扣。

算法三:对高级会员提供20%的促销折扣。

代码语言:javascript
复制
public static void main(String[] args) {
        //选择并创建需要使用的策略对象
        MemberStrategy strategy = new AdvancedMemberStrategy();
        //创建环境
        Price price = new Price(strategy);
        //计算价格
        double quote = price.quote(300);
        System.out.println("图书的最终价格为:" + quote);
    }

策略模式对多态的使用

通过让环境类持有一个抽象策略类(超类)的引用,在生成环境类实例对象时,让该引用指向具体的策略子类。再对应的方法调用中,就会通过Java的多态,调用对应策略子类的方法。从而可以相互替换,不需要修改环境类内部的实现。同时,在有新的需求的情况下,也只需要修改策略类即可,降低与环境类之间的耦合度。

策略模式和工厂方法的异同

工厂模式和策略模式的区别在于实例化一个对象的位置不同,对工厂模式而言,实例化对象是放在服务端的,即放在了工厂类里面; 而策略模式实例化对象的操作在客户端

工厂模式要求服务端的销售部门足够灵敏,而策略模式由于对策略进行了封装,所以他的销售部门比较傻,需要客户提供足够能区分使用哪种策略的参数,而这最好的就是该策略的实例了。

JDK

  • java.util.Comparator#compare()
  • javax.servlet.http.HttpServlet
  • javax.servlet.Filter#doFilter()

模板方法(Template Method)

https://www.jianshu.com/p/cc391b56bd0e

典型用例:Spring

定义

模板方法模式是类的行为模式。

准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。

结构

模板方法中的方法可以分为两大类:模板方法和基本方法。

模板方法

一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。

一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。

基本方法

基本方法又可以分为三种

  • 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
  • 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
  • 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。

默认钩子方法

一个钩子方法常常由抽象类给出一个空实现作为此方法的默认实现。这种空的钩子方法叫做“Do Nothing Hook”。具体模版类中可以选择是否重写钩子方法,通常重写钩子方法是为了对模版方法中的步骤进行控制,判断钩子方法中的状态,是否进行下一步操作。

使用场景

模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式广泛应用于框架设计中,以确保通过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。

JDK

  • java.util.Collections#sort()
  • java.io.InputStream#skip()
  • java.io.InputStream#read()
  • java.util.AbstractList#indexOf()

访问者(Visitor)

https://www.jianshu.com/p/80b9cd7c0da5

什么是访问者模式?

比如我有一个账单,账单有收入,支出两个固定方法。但是访问账单的人不确定,有可能是一个或者多个。

访问者模式有两个特点

一般被访问的东西所持有的方法是固定的,就像账单只有收入和支出两个功能。而访问者是不固定的。

数据操作与数据结构相分离:频繁的更改数据,但不结构不变。比如:虽然每一天账单的数据都会变化(数据变化),但是只有两类数据,就是支出和收入(结构不变)。

代码

见参考网页

空对象(Null)

意图

使用什么都不做的空对象来代替 NULL。

一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。

-----正文结束-----

更多精彩文章,请查阅我的博客或关注我的公众号:Rude3Knife

全复习手册文章导航

点击公众号下方:技术推文——面试冲刺

全复习手册文章导航(CSDN)

知识点复习手册文章推荐

关注我

我是蛮三刀把刀,目前为后台开发工程师。主要关注后台开发,网络安全,Python爬虫等技术。

来微信和我聊聊:yangzd1102

Github:https://github.com/qqxx6661

原创博客主要内容

  • 笔试面试复习知识点手册
  • Leetcode算法题解析(前150题)
  • 剑指offer算法题解析
  • Python爬虫相关技术分析和实战
  • 后台开发相关技术分析和实战

同步更新以下博客

1. Csdn

http://blog.csdn.net/qqxx6661

拥有专栏:

  • Leetcode题解(Java/Python)
  • Python爬虫实战
  • Java程序员知识点复习手册

2. 知乎

https://www.zhihu.com/people/yang-zhen-dong-1/

拥有专栏:

  • Java程序员面试复习手册
  • LeetCode算法题详解与代码实现
  • 后台开发实战

3. 掘金

https://juejin.im/user/5b48015ce51d45191462ba55

4. 简书

https://www.jianshu.com/u/b5f225ca2376

个人公众号:Rude3Knife

如果文章对你有帮助,不妨收藏起来并转发给您的朋友们~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端技术漫谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 面试知识点复习手册
  • 快速回忆
  • 理念
    • 高内聚低耦合
    • 行为型
      • 责任链(Chain Of Responsibility)
        • 意图
        • 职责和角色
        • 设计使用责任链的基本流程
        • 应用实例
        • 实现举例
        • JDK
      • 命令(Command)
        • 意图
        • 应用实例
        • 类图
        • JDK
      • 解释器(Interpreter)
        • 意图
        • 应用实例
        • JDK
      • 迭代器(Iterator)
        • 意图
        • 迭代器模式的优缺点
        • JDK
      • 中介者(Mediator)
        • 意图
        • 实现
        • JDK
      • 备忘录(Memento)
        • 意图
        • 主要解决
        • 应用实例
        • JDK
      • 观察者(Observer)
        • 意图
        • 何时使用
        • 实现举例
        • JDK
      • 状态(State)
        • 意图
        • 应用实例
      • 策略(Strategy)
        • 意图
        • 具体场景实现
        • 策略模式对多态的使用
        • 策略模式和工厂方法的异同
        • JDK
      • 模板方法(Template Method)
        • 定义
        • 结构
        • 基本方法
        • JDK
      • 访问者(Visitor)
        • 什么是访问者模式?
        • 访问者模式有两个特点
        • 代码
      • 空对象(Null)
        • 意图
        • 原创博客主要内容
        • 个人公众号:Rude3Knife
    • -----正文结束-----
    • 关注我
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档