前言
本篇是讲述之前学习设计模式的一个总结篇,其目的是为了对这些设计模式的进行一个提炼总结,能够通过查看看此篇就可以理解一些设计模式的核心思想。
设计模式简介
什么是设计模式
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
为什么使用设计模式
使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式类型
设计模式有23种类型。按照主要分类可以分为三大类:
一、创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 单例模式
- 工厂模式
- 抽象工厂模式
- 建造者模式
- 原型模式
二、结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
- 适配器模式
- 桥接模式
- 过滤器模式
- 组合模式
- 装饰器模式
- 外观模式
- 享元模式
- 代理模式
三、行为型模式
这些设计模式特别关注对象之间的通信。
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 空对象模式
- 策略模式
- 模板模式
- 访问者模式
设计模式的原则
设计模式的六大原则
- 开闭原则:对扩展开放,对修改关闭。
- 里氏代换原则:对开闭原则的补充。任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。
- 依赖倒转原则:针对接口编程,依赖于抽象而不依赖于具体。
- 接口隔离原则:尽量使用多个隔离的接口,为了降低类之间的耦合度。
- 迪米特法则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
- 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承。
设计模式之间的关系图:
设计模式总概况:
设计模式相关文章:
创建型模式
单例模式
单例模式介绍
核心就是保证一个系统中的某个类只有一个实例而且该实例易于外界访问。
单例模式的使用场景
在程序中比较常用的是数据库连接池、线程池、日志对象等等。
单例模式使用
单例模式的写法主要有5种,分别是:
- 饿汉式: 简单安全, 效率低;
- 饱汉式: 简单不安全, 效率高 ;
- 静态内部类: 安全, 效率高;
- 双重锁检查: 复杂安全, 效率高;
- 枚举单例:简单安全, 效率高;
单例模式示例图
单例模式总结
- 构造方法私有化(private);
- 定义一个私有(private)静态(static)实例化对象;
- 对外提供一个公共(public)静态(static)的方法得到该实例;
工厂模式
工厂模式主要有三种,简单工厂模式、工厂方法模式和抽象工厂模式。但是一般的情况下我们主要用到的是工厂方法模式和抽象工厂模式。
工厂方法模式介绍
其核心是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂方法模式使用场景
比如生活中的汽车制造,大名鼎鼎的hibernate框架在选择数据库方言这块。
工厂方法模式示例图
抽象工厂模式介绍
主要核心是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式使用场景
比如生活中的服装制造厂,可以单独制造衣服、裤子、袜子等等,也可以生产一套服装。
抽象工厂模式示例图
建造者模式
建造者模式介绍
使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
简单的来说就是将一个复杂的东西抽离出来,对外提供一个简单的调用,可以在同样的构建过程创建不同的表示。和工厂模式很相似,不过相比而言更加注重组件的装配。
建造者模式使用场景
适用一些基本组件不便,但是组合经常变化的时候。比如超市促销的大礼包。
建造者模式角色
Builder:指定一个抽象的接口,规定该产品所需实现部件的创建,并不涉及具体的对象部件的创建。
ConcreteBuilder:需实现Builder接口,并且针对不同的逻辑,进行不同方法的创建,最终提供该产品的实例。
Director:用来创建复杂对象的部分,对该部分进行完整的创建或者按照一定的规则进行创建。
Product:示被构造的复杂对象。
建造者模式优缺点
优点:
- 建造者独立,易扩展。
- 便于控制细节风险。
缺点
- 内部结构复杂,不易于理解。
- 产品直接需要有共同点,范围有控制。
建造者模式示例图
原型模式
原型模式介绍
用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。核心是克隆。
原型模式使用场景
- 类初始化的时候需要消耗大量资源的时候;
- 获取数据库连接繁琐的时候;
- 一个对象,有很多个修改者的时候;
原型模式优缺点
优点:
1.可以提升性能;缺点:
1.因为必须实现Cloneable 接口,所以用起来可能不太方便。原型模式示例图
结构型模式
适配器模式
适配器模式介绍
适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。简单的来说就是通过某个接口将不兼容的两个类进行兼容,俗称转换器。
适配器模式使用
适配器模式主要有两种类型,一种是类适配器模式,主要通过继承来实现适配器功能;一种是对象适配器模式,通过组合来实现适配器功能。
适配器模式使用场景
电器的电压,经典的jdbc使用。
适配器模式优缺点
优点:
提升了类的复用和灵活度。
缺点:
使用过多,系统会比较杂乱,难以把握。
适配器模式示例图
桥接模式
桥接模式介绍
桥接是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。通过一个中间的桥梁对两边的东西进行关联起来,但是关联的两者之间又不相互影响。
桥接模式场景
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
桥接模式优缺点
优点:
1、抽象和实现的分离,实现了解耦;
2、提升的扩展能力。
缺点:
会使系统看起复杂,对新手不友好,没有一定的抽象进行设计能力难以理解。
桥接模式示例图
外观模式
外观模式介绍
外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。它向现有的系统添加一个接口,来隐藏系统的复杂性。对外提供一个简单接口,隐藏实现的逻辑。
外观模式使用场景
系统中有多个复杂的模块或者子系统的时候。
外观模式优缺点
优点:
降低了耦合,从某种方面来说也提升了安全性。
缺点:
不符合开闭原则,不易更改。
外观模式示例图
装饰器模式
装饰器模式介绍
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
把某个东西进行装饰起来,让它可以提供一些额外的功能。
装饰器模式使用场景
原型不变,动态增加一些功能的时候。
装饰器模式优缺点
优点:
装饰类和被装饰类可以独立发展,耦合度低,易于扩展,灵活方便。
缺点:
过多的对某个类进行装饰,会增加复杂度。
装饰器模式示例图
组合模式
组合模式介绍
组合模式是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
组合模式使用场景
可以表示为 ‘部分-整体’的层级结构。
组合模式优缺点
优点:
高层模块调用较为简单,增加某个节点方便。
缺点:
因为其子节点的声明都是实现类,而不是接口,违反了依赖倒置原则。
组合模式示例图
过滤器模式
过滤器模式介绍
过滤器模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
过滤器模式使用场景
需要进行筛选的时候。
过滤器模式优缺点
优点:
简单,解耦,使用方便。
缺点:
过多使用需要注意性能。
过滤器模式注意事项
在jdk1.8以后可以使用steam的方法进行过滤分组,可以根据指定的条件进行过滤分组筛选。
享元模式
享元模式介绍
享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式角色
享元模式的角色主要分为三大类,抽象享元类、具体享元类以及享元工厂类。
- 抽象享元类:所有具体享元类的超类或者接口,通过这个接口,可以接受并作用于外部专题。
- 具体享元类:实现抽象享元类接口的功能并增加存储空间。
- 享元工厂类:用来创建并管理抽象享元类对象,它主要用来确保合理地共享。每当接受到一个请求是,便会提供一个已经创建的抽象享元类对象或者新建一个。 享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在 ,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。
享元模式使用场景
系统有大量相似对象。
享元模式优缺点
优点:
极大的减少对象的创建,从而降低了系统的内存,提升了效率。
缺点:
提高了系统的复杂度,因为需要将状态进行分离成内部和外部,并且也使外部状态固有化,使得随着内部状态的变化而变化,会造成系统的混乱。
享元模式注意事项
需要注意划分外部状态和内部状态,否则可能会引起线程安全问题。 这些类必须有一个工厂对象加以控制。
与单例模式比较
虽然它们在某些方面很像,但是实际上却是不同的东西,单例模式的目的是限制创建多个对象,避免冲突,比如使用数据库连接池。而享元模式享元模式的目的是共享,避免多次创建耗费资源,比如使用String类。
享元模式示例图
代理模式
代理模式介绍
主要是通过一个类代表另一个类的功能。通常,我们创建具有现有对象的对象,以便向外界提供功能接口。
代理模式角色
代理模式主要由这三个角色组成,抽象角色、代理角色和真实角色。
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
代理模式使用
代理模式又分为静态代理、动态代理。
- 静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
- 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。可以使用JDK中
java.lang.reflect
来进行开发。
代理模式使用场景
1、远程代理。
2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(SmartReference)代理。
代理模式优缺点
优点:
1、职责清晰。
2、高扩展性。 3、智能化。
缺点:
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式注意事项
和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
代理模式示例图
行为型模式
责任链模式
责任链模式介绍
责任链模式顾名思义,就是为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
简单的理解的话就是进行层级处理。
责任链模式角色
责任链模式主要由这三个角色组成,请求接收者接口(Handler)、请求实现者类(ConcreteHandler)和请求发送者(Client)。
- 请求接收者接口:定义可以处理客户端请求事项的接口,包含“可链接下一个同样能处理请求”的对象引用。
- 请求实现者类:实现请求处理接口,并判断对象本身是否能够处理本次请求,如果不能完成请求,则交由后继者来处理。
- 请求发送者:将请求发送给第一个接收者对象,并等待请求的回复。
责任链模式使用场景
需要动态指定处理某一组请求时,在不确定接受者的的情况下,向多个对象发送请求时。
责任链模式优缺点
优点:
耦合度低,请求者和执行者并没有必然的联系;
灵活度高,可以通过内部成员来进行更改它们执行的次序; 扩展性好,Handler的子类扩展非常方便。
缺点:
会在某程度上降低程序的性能,设置不当的话可能会出现循环调用。
在链过长时,会降低代码的阅读性以及增加代码的复杂度。
责任链模式注意事项
虽然责任链模式很灵活,但是牺牲的是一定的性能,因为责任链模式是层级处理,在处理数据的有一定的延迟,所所以需要低延迟的情况下,不推荐使用责任链模式。
责任链模式示例图
命令模式
命令模式介绍
命令模式顾名思义,是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
也就是将一个请求封装成一个对象,从而可以用不同的请求对客户进行参数化。
命令模式角色
命令模式主要由这三个角色组成,命令对象(command)、命令执行对象(received)和命令请求对象(invoker)。
- 命令对象:通过接口或抽象类声明实现的方法。
- 命令执行对象:实现命令对象的方法,并将一个接收者和动作进行绑定,调用接收者相应的操作。
- 命令请求对象:用于执行这个请求,可以动态的对命令进行控制。
命令模式使用场景
如果在有类似
命令
需要指定的,就可以用命令模式,比如记录日志、撤销操作命令等。
命令模式优缺点
优点:
耦合度低,请求者和执行者并没有必然的联系;
扩展性好,Command的子类可以非常容易地扩展。
缺点:
如果命令过多的话,会增加系统的复杂度 。
命令模式示例图
解释器模式
解释器模式介绍
解释器模式顾名思义,就是对某事物进行解释。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。
解释器模式其实就是对某事物进行解释。
解释器模式角色
解释器模式主要由这四个角色组成,抽象表达式(Expression)角色、终结符表达式(Terminal Expression)角色、非终结符表达式(Nonterminal Expression)角色和环境(Context)角色。
- 抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
- 终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
- 非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- 环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
解释器模式使用场景
一个简单的语法规则需要解释的场景,比如sql。
有重复的问题的时候。
解释器模式优缺点
优点:
扩展性好,子类扩展非常方便。
实现简单。
缺点:
可使用的场景比较少;
类过多的话,会使代码臃肿,难以维护;
解释器模式示例图
迭代器模式
迭代器模式介绍
迭代器模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示,属于行为型模式。 它提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
迭代器模式角色
迭代器模式主要由这四个角色组成,迭代器角色(Iterator)、具体迭代器角色(Concrete Iterator)、容器角色(Container)和具体容器角色(Concrete Container)。
- 迭代器角色(Iterator):通过接口或抽象类声明实现的方法。
- 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
- 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
- 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。
迭代器模式使用场景
需要为聚合对象提供遍历的功能的时候。
迭代器模式优缺点
优点:
灵活度高,可以通过不同的方式遍历对象;
扩展性好,可以很方便的增加新的聚合类和迭代器类而不用修改之前的代码。
缺点:
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
迭代器模式示例图
访问者模式
访问者模式介绍
访问者模式(VisitorPattern),顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的访问者来完成对已有代码功能的提升,它属于行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
其主要目的是将数据结构与数据操作分离。
访问者模式角色
访问者模式主要由这五个角色组成,抽象访问者(Visitor)、具体访问者(ConcreteVisitor)、抽象节点(Node)、具体节点(ConcreteNode)和结构对象(ObjectStructure)。
- 抽象访问者(Visitor)角色:声明了一个或者多个方法操作,形成所有的具体访问者角色必须实现的接口。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
- 抽象节点(Node)角色:声明一个接受操作,接受一个访问者对象作为一个参数。
- 具体节点(ConcreteNode)角色:实现了抽象节点所规定的接受操作。
- 结构对象(ObjectStructure)角色:有如下的责任,可以遍历结构中的所有元素。
访问者模式使用场景
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作;
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
访问者模式优缺点
访问者模式优点:
扩展性好,可以在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能;
符合单一职责原则,通过访问者将无关的行为分离,使职责单一;
访问者模式缺点:
违反了迪米特原则,因为具体元素对访问者公布细节;
违反了依赖倒置原则,依赖了具体类,没有依赖抽象; 对象结构变化困难,若对象结构发生了改变,访问者的接口和访问者的实现也都要发生相应的改变;
访问者模式示例图
中介者模式
中介者模式介绍
中介者模式(Mediator Pattern),定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为,属于行为型模式。
其主要的目的是用来降低多个对象和类之间的通信复杂性。
中介者模式角色
中介者模式主要由这四个角色组成, 抽象中介者(Mediator)、具体中介者(ConcreteMediator)、 抽象同事类(Colleague)和具体同事类(ConcreteColleague) 。
- 抽象中介者(Mediator): 定义了同事对象到中介者对象之间的接口。
- 具体中介者(ConcreteMediator): 实现抽象中介者的方法,它需要知道所有的具体同事类,同时需要从具体的同事类那里接收信息,并且向具体的同事类发送信息。
- 抽象同事类(Colleague): 定义了中介者对象的接口,它只知道中介者而不知道其他的同事对象。
- 具体同事类(ConcreteColleague) : 每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者。
中介者模式使用场景
通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
中介者模式优缺点
优点:
灵活性高,因为将同事类进行了解耦,使其不必有关联性;
降低了类的复杂度,将一对多转化成了一对一;
缺点:
中介者使用过多,会使系统变得复杂难以维护;
中介者模式注意事项
若不明确各个类的职责,那么就不要进行使用!
和外观模式、代理模式比较
中介者模式和外观模式、代理模式比较类似,但是又有不同。
和外观模式比较,中介者模式中,同事类必须依赖与中介者,中介者也知道同事类;但是外观模式中,子系统是不需要知道外观类的存在,并且子系统是可以脱离外观模式的。 和代理模式,代理模式的核心就是代理作用,主要还是对原先的类进行扩展或增加控制,比如进行权限控制;而中介者模式主要目的是为了减少对象之前的耦合,也就是同事类直接相互独立,互不影响。
中介者模式示例图
策略模式
策略模式介绍
策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
策略模式角色
策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
策略模式使用场景
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
一个系统需要动态地在几种算法中选择一种; 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现;
策略模式优缺点
优点:
扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
灵活性好,可以对算法进行自由切换;
缺点:
使用策略类变多,会增加系统的复杂度。;
客户端必须知道所有的策略类才能进行调用;
策略模式示例图
模板模式
模板模式介绍
模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板模式,其主要的的思想就是做一个模板,提供给客户端进行调用。
模板模式角色
模板模式主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。
抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
模板模式使用场景
有多个子类共有逻辑相同的方法;
重要的、复杂的方法,可以考虑作为模板方法。
模板模式优缺点
优点:
扩展性好,对不变的代码进行封装,对可变的进行扩展;
可维护性好,因为将公共代码进行了提取,使用的时候直接调用即可;
缺点:
因为每一个不同的实现都需要一个子类来实现,导致类的个数增加,会使系统变得复杂;
模板模式注意事项
为防止恶意操作,一般模板方法都加上 final 关键词!
模板模式示例图
备忘录模式
备忘录模式介绍
备忘录模式(Memento Pattern)用于保存一个对象的某个状态,以便在适当的时候恢复对象,该模式属于行为型模式。
其主要目的是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 其主要的的思想就是备份。
备忘录模式角色
备忘录模式主要由这三个角色组成,备忘录角色(Memento)、发起人角色(Originator)和负责人(Caretaker)角色。
- 备忘录(Memento):主要的功能是包含要被恢复的对象的状态。
- 发起人(Originator):在创建的时候,会在备忘录对象中存储状态。
- 负责人(Caretaker):主要是负责从备忘录对象中恢复对象的状态。
备忘录模式使用场景
需要保存/恢复数据的相关状态场景;
备忘录模式优缺点
优点
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;
实现了信息的封装,使得用户不需要关心状态的保存细节;
缺点
非常的消耗资源;
客户端必须知道所有的策略类才能进行调用;
备忘录模示例图
状态模式
状态模式介绍
状态模式(State Pattern)属于行为型模式,其状态的对象和一个行为随着状态对象改变而改变。
其主要目的解决的是当控制一个对象状态转换的条件表达式过于复杂是的情况。把状态的判断逻辑转移到表示不同状态一系列类中,可以把复杂的判断简单化。 主要的的思想就是提供一种状态,提供给客户端进行调用。
状态模式角色
状态模式主要由环境角色(Context)、 抽象状态(State)和具体状态(Concrete State)组成。
环境角色(Context): 它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的具体状态对象来处理。
- 抽象状态角色(State): 定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
具体状态角色(Concrete State):实现抽象状态定义的接口。
状态模式使用场景
行为随状态改变而改变的场景;
条件、分支语句的代替者。
状态模式优缺点
优点:
扩展性好,将和状态有关的行为放到一起,增加新的的状态,只需要改变对象状态即可改变对象的行为即可;
复用性好,让多个环境对象共享一个状态对象,从而减少系统中对象的个数;
缺点:
使用状态模式会增加系统类和对象的个数,并且该模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;
状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
状态模式注意事项
在行为受状态约束的时候使用状态模式,而且状态不超过5个。
和策略模式比较
在学习状态模式的时候,很容易和策略模式搞混,因为它们实在是太像了,很难区分,在查阅一番资料之后,整理了如下的相同点和区别点。相同点:
- 它们很容易添加新的状态或策略,而且不需要修改使用它们的Context对象。
- 它们都符合OCP原则,在状态模式和策略模式中,Context对象对修改是关闭的,添加新的状态或策略,都不需要修改Context。
- 它们都会初始化。
- 它们都依赖子类去实现相关行为。
区别点
- 状态模式的行为是平行性的,不可相互替换的;
- 而策略模式的行为是平等性的,是可以相互替换的。
- 最重要的一个不同之处是,策略模式的改变由客户端完成;
- 而状态模式的改变,由环境角色或状态自己.
状态模式示例图
观察者模式
观察者模式介绍
观察者模式又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。。
其主要目的是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式角色
观察者模式主要由这四个角色组成,抽象主题角色(Subject)、具体主题角色(ConcreteSubject)、抽象观察者角色(Observer)和具体观察者角色(ConcreteObserver)。
- 抽象主题角色(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题角色(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- 抽象观察者角色(Observer):主要是负责从备忘录对象中恢复对象的状态。
观察者模式使用场景
需要关联行为的场景;
事件需要创建一个触发链的场景,比如监控; 跨系统的消息交换场景,比如消息队列、事件总线的处理机制。
观察者模式优缺点
优点:
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃; 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
观察者模式注意事项
如果顺序执行,某一观察者错误会导致系统卡壳,建议采用异步方式。
观察者模式示例图
空对象模式
空对象模式介绍
空对象模式(NullObject Pattern)主要是通过一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。 这样的Null 对象也可以在数据不可用的时候提供默认的行为。
其主要目的是在进行调用是不返回Null,而是返回一个空对象,防止空指针异常。
空对象模式使用场景
需要大量对空值进行判断的时候;
空对象模式优缺点
优点:
可以加强系统的稳固性,能有效防止空指针报错对整个系统的影响;
不依赖客户端便可以保证系统的稳定性;
缺点:
需要编写较多的代码来实现空值的判断,从某种方面来说不划算;
其它
在学习设计模式的时候,主要参考书籍是《大话设计模式》以及的设计模式介绍。
其实在我们学习了这么多的设计模式中,大部分可能只是听说了解过,真正经常使用的设计模式也无外乎就那几种,单例模式(数据库、线程池场景)、工厂模式(简单的crud操作中实例化的model)、策略模式(商城会员、优惠打折场景)、观察者模式(消息推送场景)、代理模式(主要是动态代理这块)、外观模式(一键调用)、装饰器模式(锦上添花场景),但是也不全这样,使用设计模式最好根据实际的场景来使用,否则可能在不合适的场景使用了不适合的设计模式而导致代码混乱。音乐推荐
往期音乐推荐
一、
我们目送 那渐渐消失的行迹云 在晃眼间消逝 总是如此短暂 如像昨天起 不变的事 始终不会改变 不该存在的东西 带着遗憾消失在指间
那鸟儿还未能展翅高飞 但它总有一天会破风驰行 遥不可及的地方仍在远处 所以只能凝视深藏的愿望 孩子们走在盛夏的铁路上 流风轻抚着他们的赤脚 远离了童年
二、
这世上大部分失落,都是因为我们自己没成为更好的自己,却奢求别人成为更好的别人。
三、
平静孤寂的调子起头,两个人分开之后的沉寂 颓废,慢慢的音阶起伏,我意识到不能这样下去,我开始改变现状,变得积极。到高潮部分 我不在颓废,我走了新的目标 我迎着太阳前进。我想这就是You的含义,你是我的一段过去
四、
扶桑画师浅溪,居泰安,喜绘鲤。院前一方荷塘,锦鲤游曳,溪常与嬉戏。 其时正武德之乱,藩镇割据,战事频仍,魑魅魍魉,肆逆于道。兵戈逼泰安,街邻皆逃亡,独溪不舍锦鲤,未去。 是夜,院室倏火。有人入火护溪,言其本鲤中妖,欲取溪命,却生情愫,遂不忍为之。翌日天明,火势渐歇,人已不见。
五、
稚儿擎瓜柳棚下,细犬逐蝶窄巷中,人间繁华多笑语,惟我空余两鬓风。
六、
散落的花,火的碎片 诱人的夏夜终结
渐渐 天空闪耀,带着谁许的愿 突然绽放的烟花 美丽如梦幻,迟来脆弱的夏天 夜空中,火花舞动在今夜 枝头落下的仲夏夜之梦 愉快的凉风,热带的夜 天空飘着细雨,在喧闹的蝉鸣时节 夏末,秋天指向路边。
七、
论如何一个人在家嗨成狗。取下耳机无限的孤独感。带上耳机卧槽我是全世界。
八、
我渴望能见你一面,但请你记得,我不会开口要求见你。这不是因为骄傲,你知道我在你面前毫无骄傲可言,而是因为,唯有你也想见我的时候,我们见面才有意义。 ———《越洋情书》
九、
就算是Believe,中间也藏了一个lie;
就算是Friend,还是免不了end; 就算是Lover,还可能会over; 就算是Wife,心里也夹杂着if; 欣慰的是:即便是Forget,也曾经get, 就算impossible,但还藏着possible。
十、
人有三样东西是无法隐瞒的,咳嗽、穷困和爱;你想隐瞒越欲盖弥彰。人有三样东西是不该挥霍的,身体、金钱和爱;你想挥霍却得不偿失。人有三样东西是无法挽留的,时间、生命和爱;你想挽留却渐行渐远。人有三样东西是不该回忆的,灾难、死亡和爱;你想回忆却苦不堪言。 ——《洛丽塔》
十一、
简单,重复,毫无华丽旋律,也无厚重悲凉的伴奏。但心偏偏就被紧紧的抓住了。一种茫然却被迫紧凑的感觉。一种不知何所处的心虚。what for?
十二、
轻吟一句情话,执笔一副情画。 绽放一地情花,覆盖一片青瓦。 共饮一杯清茶,同研一碗青砂。
挽起一面轻纱,看清天边月牙。爱像水墨青花,何惧刹那芳华。
十三、
所有人和事,自己问心无愧就好,不是你的也别强求,反正离去的,都是风景,留下的,才是人生。
以上评论来自网易云!
项目的代码
是本人在学习Java过程中记录的一些代码,也包括之前博文中使用的代码。如果感觉不错,希望顺手给个start,当然如果有不足,也希望提出。
github地址:原创不易,如果感觉不错,希望给个推荐!您的支持是我写作的最大动力!
版权声明: 作者:虚无境 博客园出处: CSDN出处: 个人博客出处: