设计模式
模式分类
创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
创建型模式
工厂方法
简介
工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
参与者
抽象产品角色(Product):定义产品的接口
具体产品角色(ConcreteProduct) :实现接口Product的具体产品类
抽象工厂角色(Creator) :声明工厂方法(FactoryMethod),返回一个产品
真实的工厂(ConcreteCreator):实现FactoryMethod工厂方法,由客户调用,返回一个产品的实例
优缺点
相对于简单工厂每次新增产品都需要修改工厂类的逻辑,工厂方法只需需要添加具体产品类和具体工厂类,更符合开-闭原则。当然缺点也是需要创建更多的类。
抽象工厂
简介
抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。可以简单理解为工厂方法的工厂只创建一个产品,而抽象工厂可以创造多个产品。
参与者
抽象产品角色(Product):定义产品的接口
具体产品角色(ConcreteProduct) :实现接口Product的具体产品类
抽象工厂角色(Creator) :声明工厂方法(FactoryMethod),返回多个产品
真实的工厂(ConcreteCreator):实现FactoryMethod工厂方法,由客户调用,返回多个产品的实例
优点
允许客户使用抽象的接口创建一组相关产品
一个具体工厂可以创建多个产品,与工厂方法模式相比,可以少产生具体工厂的类数量
缺点
当新增一个产品家族成员时就要修改抽象工厂类及其下面的具体工厂类,扩展性比较差
使用场景
当需要创建产品家族,或者需要想让创建的产品集合起来时使用
单例模式
简介
单例模式的目的是保证系统中,应用该模式的一个类只有一个实例。即一个类只有一个对象实例,并且自行实例化向整个系统提供。
构建方式
懒汉式:指全局的单例实例在第一次被使用时构建
饿汉式:指全局的单例实例在类装载时构建
优点
在内存中只有一个对象,节省内存空间
避免频繁的创建销毁对象,可以提高性能
避免对共享资源的多重占用,简化访问
适用场景
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
比如:多线程的线程池、数据库连接池、在Spring中创建的Bean实例默认都是单例模式
建造者模式
简介
又名生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
参与者
Product(产品角色): 一个具体的产品对象。
Builder(抽象建造者): 创建一个Product对象的各个部件指定的抽象接口。
ConcreteBuilder(具体建造者): 实现抽象接口,构建和装配各个部件。
Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
优点
客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
缺点
产品必须有共同点,范围有限制
如内部变化复杂,会有很多的建造类
适用场景
StringBuilder和StringBuffer的append方法,sql中的PreparedStatement,JDOM中的SAXBuilder
原型模式
简介
在原型模式中我们可以利用过一个原型对象来指明我们所要创建对象的类型,然后通过复制这个对象的方法来获得与该对象一模一样的对象实例。
参与者
Prototype(抽象原型类):声明克隆方法的接口,是所有具体原型类的公共父类,它可是抽象类也可以是接口,甚至可以是具体实现类。
ConcretePrototype(具体原型类):它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
Client(客户端):在客户类中,让一个原型对象克隆自身从而创建一个新的对象。
深克隆与浅克隆
浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。
优点
1.当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高实例的创建效率。
2.扩展性好,由于原型模式提供了抽象原型类,在客户端针对抽象原型类进行编程,而将具体原型类写到配置文件中,增减或减少产品对原有系统都没有影响。
3.原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式不需要这样,圆形模式中产品的复制是通过封装在类中的克隆方法实现的,无需专门的工厂类来创建产品。
4.可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
1.需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
2.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
适用场景
1.创建新对象成本较大(例如初始化时间长,占用CPU多或占太多网络资源),新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改。
2.系统要保存对象的状态,而对象的状态很小。
3.需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的组合状态,通过复制原型对象得到新实例可以比使用构造函数创建一个新实例更加方便。
原型管理器
原型管理器(Prototype Manager)是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。
结构型模式
适配器模式
简介
适配器模式:别名为包装器(Wrapper)模式,将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。
三种适配器模式
类适配器模式、对象适配器模式、接口适配器模式
类适配器:适配器继承适配者实现目标类
对象适配器:适配器不继承适配者,而是持有它的实例
接口适配器:适配器为抽象类,并为该接口中每个方法提供一个默认实现,该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。
参与者
Target(目标抽象类):目标抽象类定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):它可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。它是适配器模式的核心。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类包好了客户希望的业务方法。
优点
1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
2.增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
3.灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。
缺点
1.一次最多只能适配一个适配者类,不能同时适配多个适配者。
2.目标抽象类只能为接口,不能为类,其使用有一定的局限性。
适用场景
1.系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码。
2.创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。 3.需要一个统一的输出接口,而输入端的类型不可预知。
装饰器模式
简介
装饰器模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。它是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
参与者
Component:统一接口,也是装饰类和被装饰类的基本类型
ConcreteComponent:具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类
Decorator:装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化
ConcreteDecorator:具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰
优点
1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
2.增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
3.灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。
缺点
1.一次最多只能适配一个适配者类,不能同时适配多个适配者。
2.目标抽象类只能为接口,不能为类,其使用有一定的局限性。
适用场景
1.系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码。
2.创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。 3.需要一个统一的输出接口,而输入端的类型不可预知。
代理模式
简介
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
优点
1.职责清晰。 2.高扩展性。 3.智能化。
缺点
1.由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2.实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式实现方式
1.静态代理: 静态代理是代理类在编译期间就创建好了,不是编译器生成的代理类,而是手动创建的类。
2.基于JDK实现动态代理:通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用)字节码文件并实例化对象返回。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)
3.基于CGlib 动态代理模式:基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术)
4.基于Aspectj实现动态代理:(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )
外观模式
简介
外观模式(Facade),他隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口,客户对象通过一个外观接口读写子系统中各接口的数据资源。 外观模式的目的不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
参与者
1、各个子系统:实现了子系统的功能
2、外观类:包含多个子系统实例,对外暴露接口,此接口调用各个子系统方法
优点
1、减少系统相互依赖。
2、提高灵活性。
3、提高了安全性。
缺点
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
适用场景
1、为复杂的模块或子系统提供外界访问的模块;
2、子系统相互独立;
3、在层析结构中,可以使用外观模式定义系统的每一层的入口。
桥接模式
简介
桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化。 桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。
参与者
Abstraction(抽象类):用于定义抽象类的接口,定义了一个实现类接口的对象,它的角色就是桥接类。
RefinedAbstraction(扩充抽象类):一般是具体类,实现了在Abstraction中声明的抽象业务方法,并可以调用在Implementor中定义的业务方法。
Implementor(实现类接口):定义实现类的接口。
ConcreteImplementor(具体实现类):具体实现Implementor接口。
优点
1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。
2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
3、实现细节对客户透明,可以对用户隐藏实现细节。
缺点
1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
适用场景
1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
组合模式
简介
组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及组合对象。组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子。
参与者
组合部件(Component):它是一个抽象角色,为要组合的对象提供统一的接口。
叶子(Leaf):在组合中表示子节点对象,叶子节点不能有子节点。
合成部件(Composite):定义有枝节点的行为,用来存储部件,实现在Component接口中的有关操作,如增加(Add)和删除(Remove)。
实现的两种方式
1、透明式的组合模式
在Component中声明所有来管理子对象的方法,其中包括Add,Remove等。这样实现Component接口的所有子类都具备了Add和Remove方法。这样做的好处是叶节点和枝节点对于外界没有区别,它们具备完全一致的接口。
2、安全式组合模式
在Component中不去声明Add和Remove方法,那么子类的Leaf就不需要实现它,而是在Composit声明所有用来管理子类对象的方法。
优点
1、组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
2、将”客户代码与复杂的对象容器结构“解耦。
3、可以更容易地往组合对象中加入新的构件。
缺点
设计复杂,需要理清层次关系
适用场景
1、当想表达对象的部分-整体的层次结构时。
2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时。