1.创建型模式概述
1.1设计模式的分类简述
- 设计模式有两种分类方式: 1.模式目的 2.模式的作用范围
- 根据目的来分:即根据模式是用来完成什么工作来划分,可分为如下三种
- 创建型模式
- 结构型模式
- 行为型模式
- 根据作用范围来分:即根据模式主要用于类上还是用于对象上来分,可分为如下两种
- 类模式:处理类和子类之间的关系,通过继承来建立,静态(编译时便确定下来)。
- 类模式范畴:工厂方法,类适配器,模板方法,解释器
- 对象模式:处理对象之间的关系,这些关系可通过组合或聚合来实现,运行时是可变化的,动态
- 对象模式:除了类模式的四种方法其他都是对象模式
- 根据目的来分:即根据模式是用来完成什么工作来划分,可分为如下三种
1.2 创建型模式简述
- 关注点:描述“如何创建对象”
- 特点:将对象的创建和使用分离(降低系统耦合度)
- 主要有如下五类创建型模式
- 单例模式
- 原型模式
- 工厂方法
- 抽象工厂
- 建造者模式
2.单例模式
2.1 单例模式概述
- 定义:一个类只有一个实例,且该类能自行创建这个实例的一种模式。
- 特点:单例类只有一个由该单例类自行创建的实例对象,对外提供一个访问该单例的全局访问点。
- 应用场景
- 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
- 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
- 频繁访问数据库或文件的对象。
- 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
优点:
1.单例模式保证内存中只有一个实例,减少内存开销
2.避免对资源多重占用
3.可设置全局访问点,优化和共享资源的访问
缺点:
1.一般没有接口,扩展困难。违背开闭原则
2.并发测试中,单例模式不利于代码调试。且调试过程中若单例中的代码没有执行完,也不能模拟生成一个新得对象。
3.功能代码常写在一个类中,易违背单一职责原则。
2.2 结构与实现
- 私有化构造函数,静态化实例属性,向外提供一个静态公有函数用于创建/获取静态私有实例。
实现方式1-懒汉式单例
- 特点:类加载时没有生成单例,第一次调用getInstance方法时创建单例
- volatile,synchronized保证线程安全,但每次访问时都要同步-影响性能且消耗更多资源
实现示例:
public class SingletonLazy {
private static volatile SingletonLazy instance = null;
//私有构造方法
private SingletonLazy(){}
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
实现方式2-饿汉式单例
- 特点:类加载时就创建一个单例,保证在调用getInstance前单例已存在
- 类创建时就已创建好一个静态对象供系统使用且以后不再改变-线程安全
实现示例:
class SingletonHungry {
private static final SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){}
public static SingletonHungry getInstance() {
return instance;
}
}
扩展-有限多例
- 将私有静态单例改为私有静态实例列表
实现示例:
class SingletonHungryList {
private static final ArrayList<SingletonHungryList> list = new ArrayList<>();
static {
for (int i = 0; i < 10; i++) {
list.add(new SingletonHungryList());
}
}
private SingletonHungryList(){}
public static SingletonHungryList getInstance() {
int i = (int) Math.random() * 10;
return list.get(i);
}
}
2.原型模式
- 定义: 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
- 结构:抽象原型类、具体原型类、访问类
- 抽象原型类:规定了具体原型对象必须实现的接口
- 具体原型类:实现了抽象原型类的clone()方法,为可被复制的对象
- 访问类:使用具体原型类的clone()方法来复制新的对象
优点:
1.Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
2.可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
1.需要为每一个类都配置一个 clone 方法
2.clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
3.当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
2.1 原型模式实现
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,且对于非基本类型属性其仍指向原有属性所指向的对象内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆而不再指向原有对象地址。
- Java中的Object类提供了浅克隆的clone()方法,具体原型类只要实现Cloneable接口就可实现对象的浅克隆(cloneable接口就是抽象原型类)
具体实现(ShallowCopy为具体原型类):
public class Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
ShallowCopy s1 = new ShallowCopy();
ShallowCopy s2 = s1.clone();
System.out.println("s1与s2内存地址相同吗? " + (s1 == s2));
System.out.println("s1与s2的o属性地址相同吗? " + (s1.o == s2.o));
}
}
class ShallowCopy implements Cloneable {
public InCopy inCopy = new InCopy();
public ShallowCopy clone() throws CloneNotSupportedException {
System.out.println("已复制原型对象");
return (ShallowCopy) super.clone();
}
}
class InCopy implements Cloneable {
/* public InCopy clone() throws CloneNotSupportedException {
System.out.println("已复制原型对象");
return (InCopy) super.clone();
}*/
}
/*
已复制原型对象(未注解inCopy内的InCopy时,若注解则s1与s2的inCopy属性地址相同为false)
s1与s2内存地址相同吗? false
s1与s2的inCopy属性地址相同吗? true
*/
扩展-原型管理器原型模式
- 在原型基础上增加一带原型管理器的PrototypeManager类,用HashMap保存多个复制的原型,Client类可通过管理器的get方法获取复制的原型。
具体示例:用带原型管理器的原型模式来生成包含“圆”和“正方形”等图形的原型,并计算其面积。分析:本实例中由于存在不同的图形类,例如,“圆”和“正方形”,它们计算面积的方法不一样,所以需要用一个原型管理器来管理它们。