Java设计模式知识点
1.设计模式概述
GOF -- 四人组
设计模式概念 : 代码设计经验的总结,是解决特定问题的模板。为了提高代码的可重用性和可靠性。
2.设计模式分类
- 创造型设计模式
- 结构型设计模式
- 行为型设计模式
3.UML类图
- 类图元素: 类名、属性、方法
-
类名 : Name
-
属性 :
[可见性]属性名:类型[=默认值]
, (可见性: public : + ,private:- , protected : # ) -
方法 :
类图示例[可见性]名称(参数列表)[:返回类型]
-
-
接口图:
接口图示例
圆圈 + 接口名 + 方法 -
类之间的关系:
- 依赖 : --->
- 关联 :
实线
或<实线>
- 聚合 :
菱形---
部分属于整体 : 学校菱形---
老师 - 组合 :
实心菱形---
部分组成整体,有相同生命周期 - 泛化 :
三角形实线
指示类的继承关系 - 实现关系
三角形---
指示接口的实现
4.面向对象的设计原则
-
开闭原则
定义: 对拓展开放,对修改关闭
作用 : 提高代码可重用性、稳定性、可维护性。测试更方便
实现:抽象约束,封装变化 :即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
-
里氏替换原则
定义: 通俗地说: 子类可以扩展父类的功能,但不能改变父类原有的功能。尽量不要进行重写操作。
作用:
- 避免继承中重写父类的方法,造成可复用性变差
- 保证了类的拓展不会给原有系统引入新的错误,降低错误的可能性。
例子: 正方形不是特殊的矩形。
长方形有长、宽两种属性。正方形继承长方形后,长和宽总是相等。长方形中长和宽能独立变化,正方形继承长方形后,重写改变边长的函数,使得长和宽同时变化。
如果设定一个resize方法,一直增加长方形的宽度,直到增加到宽度超过高度才可以。那么如果针对子类正方形的对象调用resize方法,这个方法会导致正方形的边不断地增加下去,直到溢出为止。
此时理式原则的破环造成了程序的错误。
-
依赖倒置原则
核心思想: 面向接口和抽象类编程,不要面向实现编程。
作用:
- 降低类之间的耦合性 (直接使用接口调用函数)
- 由于抽象层一般比实现层更稳定,所以系统稳定性能提高。
例子: 顾客从不同商店购买物品。假设商店有
结算
这个方法。如果不提供接口,顾客在不同商店购物就需要调用不同商店的函数,需要修改代码。而提供一个接口后,所有商店实例都使用共同的接口Shop,在不同商店结算不需要修改代码了。 -
单一职责原则
控制类的粒度大小, 将对象解耦,提高内聚性。(耦合性: 模块间联系紧密程度,耦合性越高,模块独立性越低)
(内聚性 : 模块内各元素联系紧密程度 , 内聚性越高,独立性越高)
-
接口隔离原则
定义:一个类对另一个类的依赖应该建立在最小的接口上
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。优点:
- 模块粒度更小,系统更灵活,可维护性更高
- 专用接口能够更能体现模块功能,和层次
- 减少代码冗余
-
迪米特法则
定义: 只与你的直接朋友交谈,不跟“陌生人”说话. 软件实体之间无需通信就不应该直接通信,可以通过第三方转发.
优点:
- 降低了类之间的耦合度,提高了模块的相对独立性
实现方法:
- 从依赖者角度,只依赖该依赖的对象
- 从被依赖者的角度,只暴露该暴露的方法
类的设计上:
- 降低类成员的权限
- 不暴露类的属性成员,提供相应的访问器
-
合成复用原则
合成复用原则是 通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的. 新对象可以调用已有对象的功能,从而达到复用。
继承复用缺点:
- 父类的细节暴露给子类,破坏了封装性
- 子类父类耦合度高.不利于拓展和维护
- 复用灵活性低. 继承父类后类型已经确定.
合成复用优点:
- 维护了类的封装性.旧对象作为新对象的成员,新对象无法看到旧对象细节
- 耦合度低.
- 复用灵活性高. 如果指向旧对象的成员变量是接口,则可以动态引用实现了该接口的所有对象.
一、创建型模式
创建模式的特点: 将对象的创建和使用分离。降低系统耦合。使用者不需要关系对象的创建细节。
分类:
- 单列
- 简单工程
- 工厂方法
- 抽象工厂
- 建造者
1.1 单列模式
特点:
- 一个类只有一个实例对象
- 该对象仅由自己创建
- 单例类对外提供访问该单例的全局访问点
应用场景:
- 只需要产生一个对象的时候
- 对象需要被共享的时候。例如线程池。
实现方法:
-
懒汉法:
在类加载的时候初始化对象:线程安全。
private static final Person person = new Person() ;
-
饿汉法:
public static synchronized Person getInstance() { //在getInstance方法上加同步 if(instance==null) { instance=new Person(); } else { System.out.println("已经有一个总统,不能产生新总统!"); } return instance; }
特点:
线程安全,但多线程访问的时候效率低。getInstance被同步了。
-
双重检测:
public static Person getInstance() { if(instance==null) { synchronized{ // 实例产生后,不会再执行 if(instance ==null) instance=new Person(); //只执行一次 } } else { System.out.println("已经有一个总统,不能产生新总统!"); } return instance; }
特点: 线程安全,多线程访问的时候效率也高。
1.2 简单工厂模式
使用单一工厂产生产品。
代码简洁。但是当要增添产品的产生函数时,需要改变源码, 违反开闭原则。
1.3 工厂方法模式
定义: 定义一个创建产品对象的工厂接口。对象的产生在子类工厂中完成。
这样当添加一种产品时,添加一类实现了工厂接口的子类工厂即可。满足开闭原则。
缺点是代码复制。
模式结构:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,它同具体工厂之间一一对应。
1.4 抽象工厂模式
抽象工厂: 产生工厂的工厂。
工程模式产生同一等级产品,抽象工厂目的是产生不同产品镞
抽象工厂提供创建产品的接口,包含一组完整的产品簇的创建方法。
具体工厂实现抽象工厂接口,等于开设了一家能生产不同等级产品的工厂。
抽象产品、具体产品。
例子:华为手机、华为路由器。 小米手机、小米路由器。
抽象工厂接口定义 : 手机对象产生函数 、路由器对象产生函数。
华为工厂实现抽象工厂接口。
小米工厂实现抽象工厂接口。
1.5 建造者模式
定义: 将复杂对象的构造与表示分离。将复杂对象分解成多个简单对象,一步一步构建。产品组成部分不变,但每一部分可以灵活选择。
优点:
- 各个具体的建造者相互独立,有利于系统拓展
- 客户端不必要知道产品内部组成细节。
缺点:
- 产品组成部分必须相同 ,限制了使用范围
- 产品内部变化复杂就需要很多建造类
模式结构:
-
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个部件。
-
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
-
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
-
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
与工厂模式不同的是: 建造模式注重组装过程,而工厂模式注重创建过程
1.6 原型模式
定义: 用一个已经创建好的实例作为原型,通过复制该原型对象创建一个和原型相同或相似的对象。
优点:
利用系统中相同或相似的对象创建新的对象高效简单。
传统构造函数创建复杂消耗资源。
实现Cloneable接口即可
二、结构型模式
结构型模式描述的是 以松耦合的手段将类或对象按某种布局组成更大的结构。有类结构模式(继承机制组织接口和类)、对象结构模式(组合或聚合组织对象)
对象结构模式比类结构模式更具灵活性,满足合成复用原则。
分为7种:
- 代理模式
- 适配器模式 (有类结实现和对象结构模式)
- 桥接模式
- 装饰模式
- 外观模式
- 享元模式
- 组合模式
2.1 代理模式
定义: 由于某些原因给对象提供一个代理以控制别的对象对该对象的访问。代理对象作为访问对象与目标对象之间的中介。
优点:
- 代理模式能保护目标对象,使其对访问对象透明。
- 代理模式可以拓展目标对象的功能,满足开闭原则
- 将客户端与目标对象分离,降低了系统耦合
主要缺点:
- 用户请求处理速度会变慢
- 增加了系统的复杂度
模式结构:
-
抽象业务类: 通过接口或抽象类声明 真实业务和代理对象要实现的业务方法
-
真实业务类: 实现抽象业务类的方法。
-
代理业务类:提供了与真实主题类相同的接口,通过内部对真实业务类的引用访问、控制、拓展真正业务类的功能。
动态代理:待学!
2.2 适配器模式
定义: 将一个类的接口转换成客户需要的另一个类的接口,使得原本不兼容的接口可以兼容。 如: 网线-hub-电脑usb
实现分类:类结构模式\对象结构模式
类结构模式实现:
适配者类(网线) 适配器类(hub) 目标接口(电脑usb)
适配器类继承适配者类,以访问适配者的组件;适配器类又实现目标接口,使得客户端能通过目标接口访问适配者组件。
类结构适配器对象结构模式实现:
适配器类将适配者类作为自己的成员,通过对象访问适配者组件;适配器类又实现目标接口,使用户通过目标接口完成任务。
对象结构适配器2.3 桥接模式
定义: 将抽象与实现分离,使得其可以独立变化。使用组合关系替代继承关系实现,降低了抽象和实现的耦合度。
优点:
拓展能力强。
缺点: 聚合关系建立在抽象层,抽象层设计相对复杂。
结构与实现:
以{品牌-汽车}举例
-
抽象化角色(抽象汽车类): 定义抽象类,包含一个对实现对象的引用。
-
扩展抽象化角色(汽车类): 抽象化角色的子类,实现父类中的业务方法,通过组合关系调用实现化角色业务方法。
-
实现化角色(品牌接口):定义品牌方法。
-
具体是实现角色(品牌): 给出实现化角色接口的具体实现。
应用场景:
- 当类存在两个独立变化的维度,且两个独立的维度都需要拓展
- 不希望使用继承或因为多层次继承导致系统类极具增加
- 当一个系统需要在构建抽象化角色和具体化角色之间灵活变换时。
2.4 装饰者模式:
定义: 通过组合关系,创建一个包装对象来包裹真实对象,保持真实对象类结构不变的前提下,提供额外的功能。
如果通过继承来拓展功能,由于继承的静态特征,模块之间耦合度高,随着拓展层次增多,子类会膨胀。
模式结构与实现:
-
抽象构件角色: 定义一个抽象接口。该接口是实体构件与装饰构建的标准接口。
-
具体构件角色:实现抽象构建。是被装饰的角色。
-
抽象装饰角色: 继承抽象构件,并博抽象构件的实例,通过子类拓展具体构件的功能。
-
具体抽象角色: 继承抽像装饰,实现抽象装饰的方法,给具体构件对象添加付加功能。
JVM I/O标准大量使用装饰类。
还要学习
2.5 外观模式
定义: 对多个复杂子系统提供一个一致的接口,使得子系统更容易被外部访问。
优点:
迪米特原则的实践。使得客户角色不直接与子系统角色,而通过外观角色与子系统角色通信。
2.6 享元模式
定义: 运用共享技术来有効地支持对象的复用。减少冗余对象的创建,提高系统资源利用率。
享元模式结构:
-
抽象享元角色: 所有享元角色的基类,为具体享元规范公共接口。非享元的外部状态通过set-get注入。
-
具体享元角色: 实现抽象享元的接口
-
非享元角色:不可共享的外部状态.
-
享元工厂:负责创建和管理享元。当用户请求享元,检测系统中是否存在符合要求的享元。存在,提供给用户,不存在,创建新对象。享元的存储一般用HashMap组织。
享元模式对外部状态的依赖:
- 实现享元模式的关键是把内部状态和外部状态分离开来。有多少种内部状态的组合,
系统中便最多存在多少个共享对象 - 外部状态储存在共享对象的外部,在必要时被传入共享对象来组装成一个完整的对象
2.7 组合模式
定义: 部分-整体模式。将对象组合成树状层次结构的模式,使得用户对对象和组合对象都有一致的访问性。
优点:
- 使得客户端能一致性地处理单个对象和组合对象。简化客户端代码。
- 在组合体中加入新对象后也不必更改源码,满足开闭原则。
组合模式结构:
-
抽象构件角色: 为树叶构件和树枝构件声明公共接口,实习默认行为。透明式组合管理中,要声明管理子类的接口。安全式组合模式中,不声明管理子类接口,管理工作由树枝构件声明。
-
树叶构件角色,实现抽象构建角色的接口中的默认行为,不实现管理行为。
-
树枝构件角色,实现抽象构件角色接口中的默认和管理接口。通常包含 Add()、Remove()、GetChild() 等方法。
分类:
- 透明式组合模式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
- 安全式组合模式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
三、行为模式概述
行为模式描述多个类或对象之间怎样相互协作共同完成复杂任务。设计算法与对象之间职责的分配。
一共11钟行为模式:
-
模板方法模式: 定义一个算法骨架,将一些步骤延迟到子类。使得子类在不改变算法结构的情况下重新定义算法的某些步骤。
-
策略模式:定义一系列算法,将每个算法封装,使其可以互相替换,不影响使用算法的用户
-
命令模式:
-
职责链模式: 将请求从链钟的一个对象传到另一个对象,直到请求符合响应。
-
状态模式:
-
观察者模式:存在一对多关系时,当被观察对象改变时,通知给其他观察者对象。
-
中介者模式: 定义一个中介对象简化原有对象之间的交互、通信。
-
迭代器模式:提供一种方法顺序访问聚合对象中的一系列数据,而不保留聚合对象内部的表示。
-
访问者模式:
-
备忘录模式 : 在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它
-
解释器模式:
以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。
3.1 模板方法模式
定义: 定义包含一系列操作的算法骨架,将算法的一些操作延迟到子类中,使得子类能个性化定制特定的操作。
优点:
- 封装了不变部分,拓展可变部分。符合开闭原则,便于代码复用。
缺点:
- 是类行为模式,通过继承来拓展,使得每拓展一次就要创建一个子类。系统会变得庞大。
模式结构:
-
抽象类: 给出算法框架。由一个模板方法和若干基本方法构成。
- 模板方法: 框架
- 基本方法: 框架中的操作
- 具体方法: 不可变动部分
- 抽象方法: 由子类实现
- 钩子方法: 抽象类已实现,子类可重写,可用于改变父类行为。
-
具体子类: 实现类中的抽象方法和重写钩子方法。是模板方法的一个组成步骤。
类图:
20200715111913应用场景
- 算法步骤整体固定,部分易变。
- 子类存在多个公共行为,将公共行为抽象为模板方法。
3.2 策略模式
模式定义
将一个行为下的不同实现,封装到不同的策略类中。行为的调用在用户类,而行为的具体实现转移到了策略类中。
使得多个具体的行为实现之间可以灵活替换而不影响用户对行为的调用。
优点:
- 提供相同行为的不同实现。用户可以灵活选择
- 满足开闭原则,不修改原有代码即可灵活增加新实现。
- 算法的使用在环境类(用户) , 算法的实现转移到具体的策略类。二者分离解耦。
模式结构
- 抽象策略类: 行为的公共接口,由不同的具体行为实现,由环境(客户)调用。
- 具体策略类: 实现抽象策略类,提供具体的行为。
- 环境类: 持有对策略类的引用,最终给客户端调用。
类图
20200715113707适用场景
- 系统需要动态地在几个不同策略中选择一种时
- 策略彼此独立,且要求对客户隐藏策略细节
- 多个类有相同行为, 但具体行为不同时。
3.3 命令模式
3.4 责任链模式
模式定义
为避免请求发送者与多个请求处理者耦合,将多个请求处理者分离组织成链。请求发送时,延请求处理链传递处理请求,直到请求处理完毕。
优点
- 对象之间解耦。增加了灵活性。可以很灵活地跳转请求处理的顺序或者增加新的请求处理对象。
- 责任分担。每个请求处理对象处理一个工作。符合类的单一职责原则。
模式的结构
- 抽象处理者(Handler): 定义抽象接口。包含抽象处理方法与后继连接
- 具体请求处理者: 实现抽象处理方法。判断能否处理请求,或者传递给下一个请求处理者
- 客户角色:创建处理链。向链头提交请求。
类图
20200715140442责任链
20200715140503
3.5 状态模式
传统的状态转换需要使用if-else语句来跳转,新增一个状态就新增一个判断不符合开闭原则。
定义:
对有状态的对象,将复杂的逻辑判断提取到不同状态对象中,允许状态对象在内部状态发生改变时,改变其行为。
优点
- 将特定状态下应该进行的行为包装在不同的状态对象中,满足单一职责原则
- 减少对象之间相互依赖。
- 利于状态的拓展。通过定义新的子状态就能轻松增加新的状态转换。
状态结构
- 环境(Context): 维护了一个当前状态,将状态相关操作(包括状态转移)委托给当前状态对象。
- 抽象状态角色: 定义一个接口,用以封装环境对象中特定状态应该采取的行为。
- 具体状态角色: 实现抽象状态所对应的行为。
3.6 观察者模式
定义
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
优点
- 降低了目标与观察者之间的耦合
- 建立了触发机制
模式的结构
- 抽象目标角色: 目标类,提供了保存观察者对象的聚集对象。以及增加、删除、通知观察者对象的方法。
- 具体目标角色: 实现了抽象类中的通知方法。当其内部状态改变时,通知注册过的观察者。
- 抽象观察者: 包含一个响应方法,在被目标对象通知后调用。
- 具体观察者: 实现了响应方法。
类图
20200715145722java中内置了Observerable 抽象类和Observer接口
3.7 中介模式
定义
定义一个中介对象来封装一系列对象之间的交互。降低原有对象之间的耦合度,是迪米特法则的应用。
优点
可以将网状结构中一对多(同事对同事)的关联转化为一对一(同事对中介)的关联,提高系统灵活性。
模式的结构
- 抽象中介: 提供同事对象的注册与转发同事信息的抽象方法。
- 具体中介: 实现中介接口。定义一个List或者HashList来管理同事对象
- 抽象同事类:定义同事接口,保存中介对象,提供同事对象交互的方法
- 具体同事类: 实现同事接口。
类图
20200715152522还有一种简化的中介模式,将中介变成单列。同事类直接调用,不持有中介。
3.8 迭代器模式
定义
提供一个对象来顺序遍历聚合对象中的一系列数据,而不保留聚合对象内部表示。
优点
- 将遍历功能外化,简化了聚合类
- 通过自定义迭代器,可以支持以不同方式遍历聚合
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
模式结构
- 抽象聚合: 定义添加删除聚合对象、获得迭代器的接口
- 具体聚合实现抽象聚合,返回具体迭代器实例
- 抽象迭代器: 定义遍历聚合元素的接口。hasNext() 、next()等
- 具体迭代器觉得: 实现抽象迭代器方法,记录当前遍历位置
类图
20200715154448class ConcreteIterator implements Iterator
{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list)
{
this.list=list;
}
public boolean hasNext()
{
if(index<list.size()-1)
{
return true;
}
else
{
return false;
}
}
public Object first()
{
index=0;
Object obj=list.get(index);;
return obj;
}
public Object next()
{
Object obj=null;
if(this.hasNext())
{
obj=list.get(++index);
}
return obj;
}
}
```java
class ConcreteIterator implements Iterator
{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list)
{
this.list=list;
}
public boolean hasNext()
{
if(index<list.size()-1)
{
return true;
}
else
{
return false;
}
}
public Object first()
{
index=0;
Object obj=list.get(index);;
return obj;
}
public Object next()
{
Object obj=null;
if(this.hasNext())
{
obj=list.get(++index);
}
return obj;
}
}
3.9 备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
类图:
20200715155047