一.创建型设计模式有哪些
创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将模块中对象的创建和对象的使用分离。为了使结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
- 简单工厂模式(Simple Factory Pattern)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 生成器模式[建造者模式](Builder Pattern)
- 原型模式(Prototype Pattern)
二.创建型设计模式
2.1简单工厂模式
指由一个工厂类来负责创建其他类的实例,根据方法的你不同入参决定产出什么实例,被创建的实例通常都具有共同的父类。
源码应用
- Calendar.getInstance()就是应用了简单工厂,内部调用了createCalendar方法。通过Locale参数的不同返回不同的Calendar子类实例;
- LoggerFactory.getLogger(String name) 【org.slf4j、logback】
优点
- 使用者只需要给工厂类传入一个正确的约定好的参数,就可以获取你所需要的对象,而不需要知道其创建细节,一定程度上减少系统的耦合。
缺点
- 工厂类的职责过重,不易于扩展过于复杂的产品结构。
- 增加新产品时需要修改工厂类,违背了开闭原则
适用场景
适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于对象如何创建的逻辑不需要关心。
2.2工厂方法模式
定义一个用于创建对象的接口(或抽象类),让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
源码应用
- LoggerFactory.getILoggerFactory
优点
- 用户只需要关心其产品需要用到哪个工厂,不需要关心产品创建的细节,也不需要知道产品具体产品类的类名;
- 增加新产品时,只需要增加一个具体的工厂和具体的产品类即可,无需修改其他代码,符合开闭原则;
缺点
- 类的个数容易过多,增加系统复杂性。(每个具体的产品类都配置有一个具体的工厂);
- 类似简单工厂的选择判断还是有的,只是将简单工厂内部的逻辑判断交给了客户端代码来进行
适用场景
- 创建对象需要大量重复代码
- 客户端不需要知道产品类是怎么创建出来的
2.3抽象工厂
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类
源码应用
- Spring -- AbstractBeanFactory
优点
- 具体产品在应用层代码隔离,不需要关心产品细节。只需要知道自己需要的产品是属于哪个工厂的即可 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
缺点
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口(不符合开闭原则);
- 增加了系统的抽象性和理解难度;
适用场景
- 客户端不依赖于产品类实例如何被创建,强调的是一系列相关的产品对象(同一产品族)一起使用创建对象需要大量重复的代码。
2.4 单例模式
确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
源码应用
-
J2EE标准中,ServletContext、ServletContextConfig等;
-
在Spring框架中ApplicationContext;
-
数据库连接池等;
-
Runtime使用了饿汉式单例;
优点
- 保证内存中只有一个实例,减少了内存开销;
- 因为单例类封装了他的唯一实例,所以他可以严格控制客户怎么访问他
缺点
- 单例模式没有抽象层,很难扩展;
- 如果单例持有数据,可能因为对象长期未使用被GC导致数据丢失;
适用场景
- 需要全局唯一的类,就使用单例模式,例如一些资源。
2.5原型模式
使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
源码应用
-
JSON.parseObject()
-
Spring中 scope=“prototype”,具体见
AbstractBeanFactory#doGetBeanif(mbd.isPrototype()){ //判断scope是否 prototype // createBean使用原型模式创建的新对象 prototypeInstance = this.createBean(beanName, mbd, args); }
优点
- 可以简化对象的创建过程,层级较多的对象还可以节约系统资源,提高对象的生成效率;
- 可以基于已有对象生成新对象,部分值不在设置;
- 可以动态获取原型对象运行时的状态来创建新的对象;
缺点
- 所有的对象都要配置一个clone方法,层级较多的情况下代码量很大也更加复杂
- 对已有的类进行改造时需要修改代码 ,违背ocp原则
- 破坏单例模式
适应场景
- 从一个对象在创建另一个可定制的对象,而且不需要知道任何创建的细节就适合使用原型模式;
- 一般在初始化信息不变的情况下,克隆是最好的办法。不用重新初始化对象而是动态的获得对象的运行时状态;
深度拷贝
- 在Java中实现
cloneable接口重写clone方法后的clone是浅拷贝,对象类型的只拷贝地址不拷贝内容,需要需要深拷贝 - 方式一:属性对象也实现
cloneable接口重写clone方法,对象类型在clone方法特殊处理 【都要实现加重写繁琐】 - 方式二:使用序列化反序列化实现深度克隆,在readObject0方法中,只要有构造器就会重新new一个对象
2.6生成器模式[建造者模式]
将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
源码应用
-
Spring BeanDefinitionBuilder#getBeanDefinition()
-
Mybatis SqlSessionFactoryBuilder
-
Mybatis CacheBuilder
-
StringBuilder
- StringBuilder 为指挥者
- AbstractStringBuilder是StringBuilder的父类,通过super担当具体的建造者
- Appendable:抽象建造者,定义了创建对象的接口
- String :产品
优点:
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
适用场景
- 主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临这复杂的变化;
- 创建复杂对象的算法要独立于该对象的组成部分以及他的装配方式时适用;
和工厂的区别
- 工厂的重点在于生产什么
- 建造者的重点在于怎么建造
Builder适用于类层级结构(引自《Effective Java》)
Builder适用于类层级结构。使用平行层次结构的Builder时,各自嵌套在相应的类中。抽象类有抽象类的builder,具体类有具体类的builder。
Pizza.Builder的类型是泛型,带有一个递归类型参数。和他的抽象方法 self()一样,允许在子类中适当的进行方法链接,不需要类型转换。子类的build方法返回超类中声明的类型的子类型,这被称作协变返回类型