创建者模式(Creational Pattern)
提供了多种优雅创建对象的方法
单例模式(Singleton Pattern)
Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例)
优缺点
优点
- 由于单例模式在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显
- 由于单例模式只创建了一个 实例,所以减少了系统的性能开销,当一个对象的产生时需要较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决(在Java
EE中采用单例模式时需要注意JVM垃圾回收机制) - 单例模式可以避免对于资源的多重占用
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问
缺点
- 单例模式一般没有接口,扩展很难,若要扩展除了修改代码基本没有第二种方式可以实现
- 单例模式对于测试是不利的
- 单例模式与单一职责原则有冲突
应用场景
- 要求生成唯一序列号的环境
- 在整个项目中需要一个共享访问点或者共享数据
- 创建一个对象需要消耗的资源过多
- 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式
注意事项
首先,在高并发情况下注意单例模式的线程同步问题 其次,需要考虑对象的复制情况
单例模式的扩展
需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度
常见写法
懒汉式(线程安全)
定义私有静态对象,然后定义一个加锁的静态方法获取该对象,如果该对象为null,则定义一个对象实例并将其赋值给静态对象,这样下次就可以直接获取
定义静态对象的原因
静态属性或方法是属于类的,能够很好的保障单例对象的唯一性
饿汉式
指在类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例
懒汉式和饿汉式的区别
- 懒汉式在类中定义了单例但是并未实例化,实例化的过程是在获取单例对象的方法中实现的,即第一次获取调用懒汉式时,该对象一定为空,然后去实例化对象并赋值,这样下次就能直接获取对象了
- 饿汉式是在定义单例对象的同时将其实例化的,直接使用即可。也就是说,在饿汉模式下。在ClassLoader完成后该类的实例便已经存在于JVM之中了
静态内部类
通过在类中定义一个静态内部类,将其对象的实例定义和初始化放在内部类中完成,在获取对象时要通过静态内部类调用其单例对象
设计原因:
静态内部类在JVM中是唯一的,很好的保障了单例对象的唯一性
双重校验锁
在懒汉式的基础上做进一步优化,给静态对象的定义加上volatile锁来保障初始化时对象的唯一性
工厂方法模式(Factory Pattern)
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a
class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类)
类图
工厂模式在接口中定义了创建对象的方法,而将具体的创建对象的过程在子类中实现,用户只需要通过接口创建需要的对象即可,不用关注对象的具体创建过程。同时,不同的子类可根据需求灵活实现创建对象的不同方法
优点
- 良好的封装性,代码结构清晰
- 扩展性比较优秀
- 屏蔽产品类
- 工厂方式模式是典型的解耦框架
使用场景
- 工厂方法是new一个对象的替代品,所以在需要生成对象的地方都可以使用,但是需要慎重考虑是否要增加一个工厂类进行管理,增加代码的复杂度
- 需要灵活的、可扩展的框架时,可以考虑采用工厂模式
- 工厂模式可以用在异构项目中
- 可以使用在测试驱动开发的框架下
扩展
缩小为简单工厂模式(静态工厂模式)
一个模块仅需要一个工厂类,没有必要把它生产出来,使用静态的方法就好了升级为多个工厂类
替代单例模式
延迟初始化
延迟初始化(Lazy initialization):一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用
抽象工厂模式(Abstract Factory Pattern)
Provide an interface for creating families of related or dependent objects without specifying their concrete
classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类)
优点
- 封装性
- 产品族内的约束为非公开状态
缺点
产品族扩展非常困难
使用场景
**一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式**
注意事项
- 产品族扩展困难,但是产品等级非常容易扩展,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品的生成任务即可,也就是说横向扩展容易,纵向扩展困难
在使用中,首先通过抽象接口创建不同的工厂对象,然后根据不同的工厂对象创建不同的对象
- 首先通过将类的实例化方法私有化来防止程序通过其他方式创建该类的实例,然后通过提供一个全局唯一获取该类实例的方法帮助用户获取类的实例,用户只需也只能通过调用该方法获取类的实例
- 单例模式的设计保证了一个类在整个系统中同一时刻只有一个实例存在,主要被用于一个全局类的对象在多个地方被使用并且对象的状态是全局变化的场景下
- 单例模式为系统资源的优化提供了很好的思路,频繁创建和销毁对象都会增加系统的资源消耗,而单例模式保障了整个系统只有一个对象能够被使用,很好的节约了资源
- 注意,单例模式的类构造器函数是私有的,只能由自身创建和销毁对象,不允许除了该类的其他程序使用new关键字创建对象及破坏单例模式
建造者模式(Builder Pattern)
eparate the construction of a complex object from its representation so that the same construction process can create
different representations.(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示)
建造者模式主要用于解决软件系统中复杂对象的创建问题 建造者模式常被用于一些基本部件不会变而其组合经常变化的应用场景下
**建造者模式与工厂模式最大的区别是,建造者模式更关注产品的组合方式和装配顺序,而工厂模式更关注产品的生产本身**
设计时的角色
Builder 抽象建造者
规范产品的组建,一般由子类实现 创建一个复杂产品对象的抽象接口
ConcreteBuilder 具体建造者
实现抽象类所定义的方法,并且返回一个组建好的对象 Builder接口的实现类,用于定义复杂产品各个部件的装配流程
Director 导演类
负责安排已有模块的顺序,然后告诉Builder开始建造 构造一个使用Builder的接口
Product 产品类
通常是实现了模板方法模式,也就是有模板方法和基本方法 表示被构造的复杂对象。ConcreteBuilder定义了该复杂对象的装配流程,而Product定义了该复杂对象的结构和内部表示
优点
- 封装性
- 建造者独立,容易扩展
- 便于控制细节风险
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时可以使用该模式
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能
- 在对象的创建过程中会用到系统的一些其他对象,这些对象在产品对象的创建过程中不容易得到时,也可以采用建造者模式封装该对象的创建过程
原型模式(Prototype Pattern)
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this
prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象)
优点
性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束
应用场景
- 资源优化场景
- 性能和安全要求的场景
- 一个对象多个修改者的场景
注意事项
- 构造函数不会被执行
- 浅拷贝和深拷贝
Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝
- 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象
- 对象的clone与对象内的final关键字是有冲突的,要使用clone方法,类的成员变量上不要增加final关键字
- 指通过调用原型实例的Clone方法或其他手段来创建对象
它以当前对象为原型(蓝本)来创建另一个新对象,而无需知道创建的细节
原型模式在Java中通常使用Clone技术实现,在JavaScript中通常使用对象的原型属性来实现
创建者模式比较
创建类模式包括工厂方法模式、建造者模式、抽象工厂模式、单例模式和原型模式,它们都能够提供对象的创建和管理职责。其中的单例模式和原型模式非常容易理解,单例模式是要保持在内存中只有一个对象,原型模式是要求通过复制的方式产生一个新的对象
工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步一步地精确构造创建出一个复杂的对象
- 通过工厂方法模式生产出对象,然后由客户端进行对象的其他操作,但是并不代表所有生产出的对象都必须具有相同的状态和行为,它是由产品所决定
区别
意图不同
在工厂方法模式里,我们关注的是一个产品整体,如超人整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序,它关注的是“由零件一步一步地组装出产品对象”。简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的是产品组成部分的创建过程产品的复杂度不同
工厂方法模式创建的产品一般都是单一性质产品,而建造者模式创建的则是一个复合产品,它由各个部件复合而成,部件不同产品对象当然不同。这不是说工厂方法模式创建的对象简单,而是指它们的粒度大小不同。一般来说,工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细
抽象工厂模式VS建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品,它注重的是对零件的装配、组合、封装,它从一个细微构件装配角度看待一个对象
相对来说,抽象工厂模式比建造者模式的尺度要大,它关注产品整体,而建造者模式关注构建过程,因此建造者模式可以很容易地构建出一个崭新的产品,只要导演类能够提供具体的工艺流程。也正因为如此,两者的应用场景截然不同,如果希望屏蔽对象的创建过程,只提供一个封装良好的对象,则可以选择抽象工厂方法模式。而建造者模式可以用在构件的装配方面,如通过装配不同的组件或者相同组件的不同顺序,可以产生出一个新的对象,它可以产生一个非常灵活的架构,方便地扩展和维护系统
