设计模式总结
一 单例模式(一个对象)
- 目的
保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法 - 八种方式
(1) 饿汉式(静态常量)
(2)饿汉式(静态代码块)
(3)懒汉式(线程不安全)
(4)懒汉式(线程安全,同步方法)
(5)懒汉式(线程安全,同步代码块)
(6)双重检查(推荐使用)
(7)静态内部类(推荐使用)
(8)枚举(推荐使用) - JDK中的Runtime就是经典的单例模式
- 使用场景
需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等) - 具体内容博客
单例模式
二 工厂模式(创建对象方法)
- 三种模式
(1)简单工厂模式(静态工厂模式)
**定义一个可以实例化对象的简单工厂类,封装创建对象的代码方法(获取对象)**可以创建不同的简单工厂类。
(2)工厂方法模式
定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
(3)抽象工厂模式
定义了一个 interface,用于创建相关或有依赖关系的对象簇,而无需指明具体的类,抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。从设计层面看,抽象工厂模式就是对简单工厂模式的改进。将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。 - JDK中的Calendar类就使用了简单工厂模式
- 工厂模式小结
目的:将实例化对象的代码提取出来,放到一个类中统一管理和维护
① 创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
② 不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)
③ 不要覆盖基类中已经实现的方法。 - 工厂模式具体内容
工厂模式
三 原型模式(拷贝)
- 介绍
原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象 - 原理
通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
- spring框架中bean的创建就是使用原型模式
- 浅拷贝和深拷贝
(1)浅拷贝
① 数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,就是把属性值复制一份给新的对象
② 数据类型是引用类型的成员变量,浅拷贝会进行**引用传递,就是把成员变量的引用地址复制一份给新的对象。**这种情况下,一个对象中的成员变量发生修改,另外一个对象也会修改。
③ 浅拷贝是使用默认的clone方法来实现的
(2)深拷贝
① 重写clone方法实现深拷贝
② 使用序列化实现深拷贝(推荐) - 原型模式具体内容
原型模式
四 建造者模式(抽象创建对象过程)
- 介绍
将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象 - 四个角色
(1)Product(产品角色):一个具体的产品对象
(2)Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
(3)ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件
(4)Director(指挥者):构建一个使用Builder接口的对象,用于创建一个复杂的对象,一是隔离客户和对象的生产过程,二是负责控制产品对象的生产过程。 - 原理类图
- JDK的StringBuilder使用了建造者模式
- 建造者模式具体内容
建造者模式
五 适配器模式(目标与被适配者通过适配器转换)
- 介绍
适配器模式(Adapter Pattern)**将某个类的接口转换成客户端期望的另一个接口表示,**主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper) - 工作原理
将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容。用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
- 三种分类
(1)类适配器模式
Adapter 类,通过继承 src 类,实现 dst 类接口,完成 src->dst 的适配
(2)对象适配器模式
Adapter 类持有 src 类,实现 dst 类接口,完成 src->dst 的适配
(3)接口适配器模式
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求 - SpringMVC的HandlerAdapter使用了适配器模式
(1)源码
(2)UML类图
- 适配器模式具体内容
适配器模式
六 桥接模式(抽象与行为实现独立)
- 介绍
桥接模式是指将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,抽象类聚合实现接口类,从而可以保持各部分的独立性以及应对他们的功能扩展 - 原理类图
- JDBC的Driver使用了桥接模式
(1)源码
(2)原理类图
此处不是正规的桥接模式,没有抽象类
- 桥接模式具体内容
桥接模式
七 装饰者模式(抽象主体类和主体被装饰者和装饰者)
- 介绍
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp) - 原理
说明:
(1)装饰者模式就像打包一个快递,东西就是被装饰者,纸箱就是装饰的
(2)Component 主体:比如类似前面的 Drink
(3)ConcreteComponent :具体的主体,比如前面的各个单品咖啡
(4)Decorator: 装饰者,比如各调料
(5)表现形式:new 装饰者类(new 被装饰者类)
例如:new DataInputStream(new FileInputStream(“d://aa.txt”))
- JDK中IO结构的FilterInputStream使用装饰者模式
- 装饰者模式具体内容
装饰者模式
八 组合模式(树形结构组合对象)
- 基本介绍
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式 - 原理类图
- JDK中的的HashMap使用了组合模式
(1)源码
(2)类图
- 组合模式具体内容
组合模式
九 外观模式(过程模式,子系统,统一接口)
- 基本介绍
外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 - 原理类图
- Mybatis中Configuration创建MetaObject使用外观模式
(1)源码
(2)类图
- 外观模式具体内容
外观模式
十 享元模式(外部,内部共享状态,池)
- 基本介绍
享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象。常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。 - 原理类图
说明:
(1)FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
(2)ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
(3)UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂
(4)FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法 - JDK中Integer的valueOf()方法使用了享元模式
说明:
(1) 在 valueOf 方法中,先判断值是否在IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从 缓存池返回
(2) valueOf 方法,就使用到享元模式
(3) 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
十一 代理模式(代理对象,中介)
- 基本介绍
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。 - 原理类图
- 三种形式
(1)静态代理
① 基本介绍
定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
② 实现步骤
Ⅰ 定义一个接口或父类
Ⅱ 目标对象类实现接口或继承父类
Ⅲ 代理类实现接口或继承父类并聚合目标对象
Ⅳ 执行方法
(2)动态代理(JDK代理)
① 基本介绍
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理,代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象,动态代理也叫做:JDK 代理、接口代理
② 具体步骤
Ⅰ创建接口
Ⅱ 创建目标类实现接口
Ⅲ 创建代理类ProxyFactory聚合目标对象,并使用newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)重写invoke方法,生成代理对象
Ⅳ 执行方法
(3)Cglib代理
① 基本介绍
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理,Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
② 具体步骤
Ⅰ 自定义一个类
Ⅱ 创建代理类ProxyFactory 实现MethodInterceptor接口并重写intercept方法,聚合目标对象
Ⅲ 创建方法getProxyInstance(),在方法内通过Enhancer对象的create方法进行设置父类、回调函数获取代理对象等操作 - 代理模式具体内容
代理模式
十二 模板方法模式(模板方法,钩子函数,抽象方法)
- 基本介绍
(1)模板方法模式,又叫模板模式,在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
(2)模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理 - 原理类图
说明;
(1)AbstractClass 抽象类, 类中实现了final定义的模板方法(template),里面包括一些公共的方法,不让子类覆盖,定义了算法的骨架,具体子类需要去实现 其它的抽象方法 operationr2,3,4
(2)ConcreteClass 实现抽象方法 operationr2,3,4, 以完成算法中特点子类的步骤 - Spring IOC容器初始化使用模板方法
(1)源码
(2)类图
说明:
(1)AbstractApplicationContext为抽象类,类似于上面的SoyaMilk,含有模板方法和钩子函数
(2)refresh方法为模板方法
(3)refreshBeanFactory和getBeanFactory为抽象方法,由子类实现
(4)postProcessBeanFactory和onRefresh是钩子函数,可以由子类决定是否覆盖 - 模板方法具体内容
模板方法模式
十三 命令模式(动作的请求和执行及命令)
- 基本介绍
(1)命令模式:在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。
(2)在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作 - 原理类图
说明:
(1)Invoker 是调用者角色
(2)Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
(3)Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
(4)ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现 execut - Spring的JdbcTemplete使用了命令模式
(1)代码
(2)角色说明
① StatementCallback接口,类似于命令接口(command)
② class QueryStatementCallback implements StatementCallback, SqlProvider , 匿名内部类, 实现了命令接口,同时也充当命令接收者
③ 命令调用者 是 JdbcTemplate , 其中execute(StatementCallback action) 方法中,调用 action.doInStatement 方法. 不同的 实现StatementCallback 接口的对象,对应不同的doInStatemnt 实现逻辑
④ 实现 StatementCallback 命令接口的子类还有如图所示
- 命令模式具体内容
命令模式
十四 访问者模式(稳定的数据结构和变动的功能需求)
- 基本介绍
访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作,主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题 - 工作原理
在被访问的类里面加一个对外提供接待访问者的接口
说明:
(1)Visitor 是抽象访问者,为该对象结构中的ConcreteElement 的每一个类声明一个 visit 操作
(2)ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分
(3)ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
(4)Element 定义一个 accept 方法,接收一个访问者对象
(5)ConcreteElement 为具体元素,实现了 accept 方法 - 使用场景
访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的 - 具体内容
访问者模式
十五 迭代器模式(集合迭代遍历)
- 基本介绍
提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构 - 原理类图
说明:
(1)Iterator : 迭代器接口,是系统提供,含义 hasNext, next, remove
(2)ConcreteIterator : 具体的迭代器类,管理迭代
(3)Aggregate :一个统一的聚合接口, 将客户端和具体聚合解耦
(4)ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历集合
(5)Client :客户端, 通过 Iterator 和 Aggregate 依赖子类 - JDK的ArrayList集合使用了迭代器模式
说明:
(1)Iterator 接口系统提供
(2)List 就是充当了聚合接口,含有一个 iterator() 方法,返回一个迭代器对象
(3)内部类 Itr 充当具体实现迭代器 Iterator 的类, 作为 ArrayList 内部类
(4)ArrayList 是实现聚合接口 List 的子类,实现了 iterator()
(5)迭代器模式解决了 不同集合(ArrayList ,LinkedList) 统一遍历问题 - 具体内容
迭代器模式
十六 观察者模式(多对一依赖)
- 基本介绍
对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject通知 Observer 变化 - 案例原理类图
说明:
(1)奶站/气象局:Subject
(2)用户/第三方网站:Observer
Subject:登记注册、移除和通知
① registerObserver 注册
② removeObserver 移除
③ notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
Observer:接收输入 - JDK的Observable类使用了观察者模式
模式角色分析
① Obvervable:等价于前面的Subject,是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add… delete … notify…
② Observer 等价于前面的 Observer, 有 update
③ Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
4. 具体内容
观察者模式
十七 中介者模式(中介者对象拥有对象map集合)
- 基本介绍
用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互 - 原理类图
说明:
(1)Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
(2)Colleague 是抽象同事类
(3)ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个同事对象消息,完成相应的任务
(4)ConcreteColleague 具体的同事类,会有很多, 每个同事只知道自己的行为,而不了解其他同事类的行为(方法),但是他们都依赖中介者对象 - 具体内容
中介者模式
十八 备忘录模式(守护者保存状态)
- 基本介绍
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态 - 原理类图
说明:
(1)originator : 对象(需要保存状态的对象)
(2)Memento : 备忘录对象,负责保存好记录,即 Originator 内部状态
(3)Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
(4)如果希望保存多个 originator 对象的不同时间的状态,也可以,只需要要 HashMap <String, 集合> - 具体内容
备忘录模式
十九 解释器模式(解释表达式)
- 基本介绍
是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式) - 原理类图
说明:
(1)Context: 是环境角色,含有解释器之外的全局信息
(2)AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
(3)TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
(4)NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作 - Spring框架中的SpelExpressionParser使用了解释器模式
- 具体内容
解释器模式
二十 状态模式(状态对应行为)
- 基本介绍
它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换 - 原理类图
说明:
(1)State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
(2)Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
(3)ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为 - 具体内容
状态模式
二十一 策略模式(变与不变,策略接口组,客户主体)
- 基本介绍
(1)策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
(2)这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略) - 原理类图
客户 context 有成员变量 strategy 或者其他的策略接口
,至于需要使用到哪个策略,我们可以在构造器中指定
- JDK的Arrays的Comparator使用了策略模式
- 具体内容
策略模式
二十二 职责链模式(职责对象链)
- 基本介绍
为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。 - 原理类图
(1)Request , 含有很多属性,表示一个请求
(2)Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含有另外 Handler
(3)ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链 - SpringMVC的HandlerExecutionChain使用了职责链模式
说明:
(1)springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
(2)在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
(3)HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
(4)HandlerExecutionChain 维护了HandlerInterceptor 的集合, 可以向其中注册相应的拦截器. - 具体内容
职责链模式