文章目录
源代码:https://github.com/ouyangxizhu/design_pattern.git
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/9fd988ca76b4910aea82a45fd5a98854.png)
一、创建型
简单工厂模式(不属于GOF23)
com.ouyangxizhu.design.pattern.creational.simplefactory.VideoFactory
定义
由一个工厂对象决定创建出哪一种产品类的实例
适用场景
创建对象比较少,客户端(应用层)只知道传入工厂的参数,不关心具体实现。
优点
只需要传入一个参数,就可以创建所需要的对象,无需知道具体创建细节。
缺点
- 工厂逻辑比较重,当新增对象(产品)时需要改变工厂类的逻辑。违背开闭原则。(可以通过反射增加扩展性,通过反射的话也可以在客户端通过反射)
- 无法形成基于继承的等级结构。
客户端不创建具体的类,只传递给工厂参数。客户端只依赖对应的工厂类。
源码
java.util.Calendar#createCalendar(创建哪个国家)
ch.qos.logback.classic.LoggerContext#getLogger(java.lang.String)
Class.forName()方法也是简单工厂比如加载数据库驱动(加载什么样的数据库驱动,比如mysql等)
1. 工厂方法
com.ouyangxizhu.design.pattern.creational.factorymethod
定义
定义一个创建对象的接口,但让实现这个接口的类来实例化哪个类工厂方法让类的实例化推迟到子类中进行。
类型
创建型
适用场景
解决同一等级的产品
- 创建对象需要大量的代码。
- 客户端(应用层)不依赖于产品类实例如何被创建,实现等细节。客户端(应用层)创建能创建指定的产品的工厂。
- 一个类通过其子类来指定创建哪个对象。
优点
- 用户只需要关心产品对应的工厂,无需关心创建细节。
- 加入新产品符合开闭原则,提高可扩展性。
缺点
- 类的个数容易过多,增加复杂性。
- 增加了系统的抽象性和理解难度。
源码
Iterator接口就是一个简单工厂实现,具体的实现由Collection的子类来实现。(比如ArrayList的ITr(ITr实现Iterator接口))
org.slf4j.ILoggerFactory
2. 抽象工厂
com.ouyangxizhu.design.pattern.creational.abstractfactory
2.1 定义
抽象工厂模式提供一个创建一系列相关或者相互依赖对象的接口。无需指定他们的具体的类。
类型
创建型
适用场景
解决同一产品族的产品
- 客户端(应用层)不依赖于产品类实例如何被创建,实现等细节。依赖于某个产品族工厂。
- 强调一系列相关的产品对象(属于同一产品族)一起创建需要大量的重复代码。
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点
- 具体产品在应用层代码隔离,无需关心创建细节。只和某个产品族的工厂有关系。
- 将一个系列的产品族统一到一起创建。
缺点
- 规定了所有可能被创建的产品集合,产品族中扩展新产品困难,需要修改抽象工厂的接口。
- 增加了系统的抽象性和理解难度。
源码
java.sql.Connection(这是一个抽象类,有很多抽象方法,有java.sql.Connection#createStatement(int, int),java.sql.Connection#prepareStatement(java.lang.String, int, int))获得的一定是同一个产品族。
org.apache.ibatis.session.SqlSessionFactory的org.apache.ibatis.session.SqlSessionFactory#openSession()和org.apache.ibatis.session.SqlSessionFactory#getConfiguration等
工厂方法和抽象工厂的区别
- 工厂方法针对产品等级结构(可以理解为不同厂家生产的同一类型产品,比如美的,格力生产的空调)
- 抽象工厂针对一个产品族(可以理解为同一厂家生产的不同产品,比如美的产的空调,洗衣机等)。产品等级结构的扩展违背开闭原则,产品族的扩展不违背开闭原则。抽象工厂的方法经常以工厂方法的形式实现。是面向抽象编程。
简单工厂和抽象工厂关注的是某个产品的不同维度。
其实用简单工厂的反射模式创建对象是很好的,即使添加属性等也无所谓。但是反射的性能不是很好。
3. 建造者模式
com.ouyangxizhu.design.pattern.creational.builder.v2
定义:
将一个复杂对象的构建与他的表示分离,是的同样的构建过程可以创建不同的表示。
类型
创建型
适用场景
当一个对象有很多属性时,并且有很多属性可以为null,正常的话需要写很多构造方法,用建造者模式解决。因为构造器不能有返回值,但是建造者模式可以为每一个属性设置一个方法,返回建造者对象本身,返回本身的好处是可以链式调用。之后在要创建的对象的构造器中传入建造者,这样可以链式调用设置属性,不用写那么多要建造的对象的构造器。最核心的就是返回建造者本身。
- 一个对象有很多属性,有非常复杂的内部结构。
- 把复杂对象的创建和使用分离。
优点
- 封装型好,创建和使用分离。
- 扩展性好,建造类之间独立,一定程度上解耦。
缺点
- 产生多于的builder对象
- 产品内部发生变化,建造者也需要修改,成本较大。
源码
java.lang.StringBuilder#append(java.lang.String)append可以链式调用。
org.apache.ibatis.session.SqlSessionFactoryBuilder
建造者和工厂模式的比较
建造者模式更注重方法的调用顺序,工厂模式注重创建产品。
建造者模式可以创建一些复杂的产品,有各种复杂部件组成,工厂创建出来都是一个样子。
工厂模式关注的是创建出来就行,而建造者需要知道这个产品有哪些部件组成。
如果一定的顺序产生的结构不一样的话,用建造者调整,工厂不关注顺序。
4. 单例模式
com.ouyangxizhu.design.pattern.creational.singleton
定义
保证一个类只有一个实例,并提供一个全局访问点。
优点
- 内存只有一个实例,减少了内存开销。
- 可以避免对资源的多重占用。比如只有一个文件写实例,可以避免对同一个资源文件的写操作。
- 设置全局访问点,严格控制访问。
缺点
- 没有接口,扩展很困难。
单例重点
- 私有构造器
- 线程安全。
- 延迟加载。
- 序列化和反序列化安全
- 防止反射攻击
破坏单例
饿汉模式没有办法防止放射攻击(因为反射调用setAccessible()方法修改权限)但是可以重写readResolve()方法防止序列化反序列化攻击(其实也是反射,但是ReadObject0()方法的hasreadResolveMethod()方法,有的话通过反射直接返回readResolve()方法对象)。
懒汉模式可以防止序列化攻击(重写readResolve()方法)和反射攻击(可以在私有构造器里面判断是否为null,不为null的时候抛出异常。这种方式只有在类初始化时创建对象的方法有用,因此懒汉模式不好用)
enum的单例模式天然可以防止序列化和反射破坏(是JVM保证的,里面有判断方法。判断是否是枚举,要是枚举的话不允许调用构造器)。
源码
IOC容器
java.lang.Runtime 饿汉
org.springframework.beans.factory.config.AbstractFactoryBean#getObject
org.apache.ibatis.executor.ErrorContext#instance------ThreadLocal 记录本线程的错误。
线程池,连接池。可以把工厂类设计为单例。可以结合享元模式和单例模式。
5. 原型模式
com.ouyangxizhu.design.pattern.creational.prototype
定义
原型实例指定创建对象的种类,通过拷贝这些原型创建新的对象。
特点
不需要知道任何创建细节,不调用构造函数
适用条件
- 类初始化消耗很多资源
- new产生一个对象需要非常繁琐的过程(数据准备,访问权限)
- 构造函数比较复杂
- 循环体中生产大量对象
优点
- 原型模式比new一个对象性能高
- 简化创建过程
缺点
必须配备clone方法。
对克隆复杂对象或者克隆出的对象进行复杂改造时,容易引入风险。
深拷贝、浅拷贝要运用得当。
源码
Arraylist和HashMap都重写了clone()方法,ArrayList通过调用Arrays.copyOf()方法克隆,是深克隆,Arrays.copyOf()调用System.copyOf()方法。
深克隆和浅克隆区别
- 浅克隆,只克隆对象表面,但是内部引用类型成员对象不克隆(即克隆后对象是不一样的,但是内部属性是一样的,如果改源对象的内部对象属性,克隆后的该属性也会改)
- 深克隆代表对象本身和对象内部的属性都会克隆(可以在实现clonable接口后重写的clone()方法内部,调用对象属性的克隆方法)
Object类的clone()方法是浅克隆(native()方法)。
通过clone破坏单例模式
通过反射克隆方法可以破坏单例,防止办法是不继承Cloneable()接口,不写clone()方法,或者在clone方法里面调用获取单例的方法getInstance()。