创建型设计模式
主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码合使用代码。
单例模式
单例模式用来创建全局唯一的对象。一个类只允许创建一个对象,这个类就是一个单例类。单例有几种经典实现方式,分别为:饿汉式、懒汉式、双重检测、静态内部类、枚举。
工厂模式
工厂模式包含简单工厂、工厂方法、抽象工厂这三种细分模式。其中,前两者比较常用、后一种并不常用。
工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定参数来决定创建哪种类型的对象。
实际上,如果创建对象逻辑不复杂,直接new就可以了,不需要使用工厂模式。当创建逻辑比较复杂时,考虑使用工厂模式,封装对象的创建过程,将对象创建与使用相分离。
当对象创建逻辑比较简单时,推荐使用简单工厂模式,将多个对象创建逻辑放到一个类中。
当对象创建逻辑比较复杂时,推荐使用工厂方法模式,避免设计一个过于庞大的工厂类,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
工厂模式的作用有下面4个:
1.封装变化。将创建逻辑的变更对调用者透明。
2.代码复用。创建代码抽离到独立的工厂类后可以复用。
3.隔离复杂性。封装创建,调用者无需了解如何创建。
4.控制复杂度。将创建代码抽离,让原本函数或类职责更加单一。
建造者模式
建造者模式用来创建复杂对象,可以通过设置不同的可选参数,定制化地创建不同的对象。
如果有下面情况,可以考虑使用建造者模式:
1.如果类的属性之间有一定的依赖关系或者约束条件;
2.如果希望创建不可变对象,也就是说,对象创建好之后,不能再修改内部的属性值,这样就不能在类中暴露set方法。
原型模式
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同)。这种情况下,我们可以利用已有对象(原型)进行拷贝的方法,来创建新对象,以达到节省创建时间的目的。
这种基于原型来创建对象的方式就叫做原型模式。
原型模式有两种实现方法:深拷贝和浅拷贝。
浅拷贝只会复制类中的基本数据类型数据和引用对象的内存地址;
深拷贝得到的是一份完全独立的对象。所以更加耗时,耗内存空间;
如果要拷贝的对象时不可变对象,浅拷贝共享不可变对象是没问题的。但是对于可变对象来说,浅拷贝得到的对象和原始对象会共享部分数据,这有可能出现数据被修改的风险。
所以,一般比较推荐使用深拷贝。不要为了一点性能提升使用浅拷贝。
结构型设计模式
结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典结构可以解决特定应用场景的问题。
代理模式
代理模式在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是他和装饰器模式最大的不同。
一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类没有定义接口,我们可以让代理类继承原始类的方法来实现代理模式。
静态代理需要针对每个类都创建一个代理类,增加了维护和开发成本。
我们可以使用动态代理:不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
代理模式常用于开发一些非功能性需求,比如监控、统计、鉴权、限流、事务、幂等、日志。
我们可以将这些附加功能与业务功能解耦,放到代理类统一处理。
装饰器模式
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承,给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要依据。除此之外,装饰器模式还有一个特点,就是可以对原始类嵌套使用多个装饰器。为了满足这样的需求,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。
适配器模式
代理模式、装饰器模式提供的都是和原始类相同的接口,而适配器提供跟原始类不同的接口。适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
适配器模式有两种实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
适配器模式是一种事后补救策略,用来补救设计上的缺陷。
门面模式
门面模式通过封装细粒度的接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性,或者解决性能、分布式事务等问题。
组合模式
组合模式用来处理树形结构数据。
组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看作树中的节点,以统一处理逻辑,并且他利用树形结构的特点,递归地处理每个子树,依次简化代码实现。
享元模式
享元,顾名思义就是被共享的单元。
享元模式的意图是复用对象,节省内存,前提是享元模式是不可变对象。
当一个系统中存在大量重复对象,我们可以利用享元模式,将对象在内存中只保留一份实例,共多处代码引用,这样可以减少内存中对象的数量,节省内存。
对于相似对象,我们也可以将这些对象中相同的部分(字段),提取出来设计成享元,让这些大量相似对象引用这些享元。
行为型设计模式
行为型设计模式主要解决的是“类或对象之间的交互”问题。
观察者模式
观察者模式将观察者和被观察着解耦。
不同应用场景下,观察者模式有着不同的实现方式:
- 有同步阻塞、以及异步非阻塞方式;
- 有进程内方式,也有跨进程方式;
同步阻塞主要是为了代码解耦;异步非阻塞除了能解耦代码外,还能提高代码的执行效率;
进程间的观察者模式解耦更加彻底,一般基于消息队列实现。
模板模式
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体接口的情况下,重新定义算法中的某些步骤。
模板模式有两大作用:服用和扩展。其中服用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
策略模式
策略模式定义一组算法类,将每个算法分别封装起来,让他们可以互相替换。策略模式可以使算法的变化独立于使用他们的客户端。策略模式用来解耦策略的定义、创建、使用。
策略类的定义包含一个策略接口和一组实现这个接口的策略类。策略的创建由工厂类来完成,封装创建细节。策略模式包含一组策略可选,客户端代码选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,后者是策略模式最典型的应用场景。
运用策略模式最常见的场景是来避免冗长的if-else或switch分支判断。
职责链模式
在职责链模式中,多个处理器依次处理同一个请求。请求先经过A处理器处理,然后传递给B处理器,B处理器完成后再传递给C处理器,以此类推,形成一个链条。链条上的每个处理其各自承担其处理职责。
职责链模式常用在框架开发中,用来实现过滤器、拦截器功能,让框架使用者在不需要修改框架源码的情况下,添加新的过滤、拦截功能。
迭代器模式
迭代器模式也叫做游标模式,它用来遍历集合对象。迭代器模式的主要作用是解耦容器代码和遍历代码。
在通过迭代器遍历集合元素时,增删集合中的元素,可能会导致某个元素被重复遍历或遍历不到。解决这个问题有两种办法:
1.遍历的时候不允许增删元素;
2.增删元素之后让遍历报错。Java语言就是采用这种解决方案,fail-fast。
状态模式
状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。
状态机又叫有限状态机,它由3个部分组成:状态、时间、动作。其中,事件也被称为转移条件。事件触发状态的转移及动作的执行。这里,动作不是必须的,可以只转移状态。
状态机有三种实现方式:
1.分支逻辑法。if-else或switch实现,直译每个状态转移。适用于比较简单的状态机。
2.查表法。对于状态很多、且转移比较复杂的状态机,通过二维数组来表示状态转移图,能极大的提高代码可读性以及可维护性。
3.利用状态模式。对于状态不多、转移也比较简单,但事件触发执行的动作包含的业务逻辑比较复杂的状态机,首选这种方式。
访问者模式
访问者模式允许一个或多个操作应用到一组对象上,设计意图是解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。
备忘录模式
备忘录模式也叫做快照模式,具体来说就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。
这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。
命令模式
命令模式用到最核心的实现手段,就是将函数封装成对象。
在大部分编程语言中,函数式没办法作为参数传递给其他函数的,也没法赋值给变量。借助命令模式,我们将函数封装成对象,这样就可以实现把函数像对象一样使用。
命令模式的主要作用和应用场景,使用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等。
解释器模式
解释器模式为某个语言定义它的语法表示,并定义一个解释器用来处理这个语法。
解释器模式的核心实现思想是,将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。
中介模式
中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多的网状关系转换为一对多的星状关系。原来一个对象需要跟N个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互关系,降低代码复杂度。