目录
(一) 原型模式
1. 原型模式概述
原型模式(Prototype Pattern): 用于创建重复的对象,同时又能保证性能. 用原型对象实例 指定创建对象的种类(需要拷贝的对象), 并且通过拷贝这些原型, 创建新的对象, 允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节
工作原理: 这种模式是实现了一个原型接口(Cloneable),该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式
创建对象有两种方式: new 和 clone
Java 中的 Object类默认提供了一个 protected 修饰的本地 clone克隆方法
protected native Object clone() throws CloneNotSupportedException;
// 覆盖重写
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
- protected: 四大修饰符之一, 受保护的. 此修饰符修饰的方法, 必须继承这个类, 才能调用这个clone方法
- native: 本地原生, 说明这个方法的实现不是在java中, 而是由C/C++实现, 并编译成.dll文件, 由java调用
- 使用 clone() 克隆方法, 必须要实现 Cloneable接口(标志性接口)
2. 原型模式-浅拷贝
克隆羊类: 原型对象, 实现Cloneable接口
public class Sheep implements Cloneable {
private String name; // 克隆羊的名称
private Integer age; // 克隆羊的年龄
private String color; // 克隆羊的颜色
private Sheep childSheep; // 克隆羊的后代
// 覆盖重写Object父类的 clone克隆方法
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}
测试
public static void main(String[] args) {
// 克隆羊多莉 原型对象
Sheep sheep = new Sheep("克隆羊多利",2, "白色");
sheep.setChildSheep(new Sheep("多利的孩子",0, "白色"));
// 原型模式 克隆
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
System.out.println("sheep1:" + sheep1 + ", hashCode: " + sheep1.hashCode() + ";\tsheep1.childSheep:" + sheep1.getChildSheep() + ", childSheep.hashCode: " + sheep1.getChildSheep().hashCode());
System.out.println("sheep2:" + sheep2 + ", hashCode: " + sheep2.hashCode() + ";\tsheep2.childSheep:" + sheep2.getChildSheep() + ", childSheep.hashCode: " + sheep2.getChildSheep().hashCode());
System.out.println("sheep3:" + sheep3 + ", hashCode: " + sheep3.hashCode() + ";\tsheep3.childSheep:" + sheep3.getChildSheep() + ", childSheep.hashCode: " + sheep3.getChildSheep().hashCode());
System.out.println("sheep4:" + sheep4 + ", hashCode: " + sheep4.hashCode() + ";\tsheep4.childSheep:" + sheep4.getChildSheep() + ", childSheep.hashCode: " + sheep3.getChildSheep().hashCode());
}
从测试打印的数据来看, 克隆出来的Sheep对象 的 childSheep对象的hashCode值相等. 意味着, 他们指向同一份内存地址, 也意味着, 此时的原型模式, 拷贝的方式是: 浅拷贝
- 对于数据类型是基本数据类型的成员变量, 浅拷贝会直接进行值传递, 也就是将改属性值复制一份给新的对象
- 对于数据类型是引用数据类型的成员变量, 比如成员变量是数组、类对象等, 那么浅拷贝会进行引用传递 也就是只将该成员变量的内存地址复制一份给新的对象. 在这种情况下, 改变其中一个对象的成员对象会影响到另一个对象的成员对象
3. 原型模式-深拷贝
深拷贝概述:
- 复制对象的所有基本类型的成员变量值
- 为所有引用类型的成员变量申请新的存储空间, 来存储引用类型的成员变量. 对象进行深拷贝要对整个对象进行拷贝
深拷贝实现方式:
-
重写clone方法来实现深拷贝: 对引用类型的成员变量单独处理(clone)
@Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep) super.clone(); // 对引用类型的成员变量chdildSheep单独处理(clone) if (sheep.childSheep != null) { sheep.childSheep = (Sheep) childSheep.clone(); } } catch (CloneNotSupportedException e) { e.printStackTrace(); } return sheep; }
-
通过对象序列化实现深拷贝(可借助第三方工具类实现 FastJosn), 下面的代码使用java原生实现对象序列化
public class Sheep implements Serializable { private String name; private Integer age; private String color; private Sheep childSheep; /** * 对象序列化深度拷贝 * * @return */ public Object deepClone() { ByteArrayOutputStream bos = null; // 字节数组输出流 ObjectOutputStream oos = null; // 对象输出流(序列化流) ByteArrayInputStream bis = null; // 字节数组输入流 ObjectInputStream ois = null; // 对象输入流(反序列化流) try { // 序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 将当前this对象以对象流的方式输出 给 bos // 反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); Sheep copyObj = (Sheep) ois.readObject(); // 从 bis中 读取(反序列化)对象 return copyObj; } catch (Exception e) { e.printStackTrace(); } finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } }
4. 总结
原型模式的注意事项和细节
- 创建新的对象比较复杂时, 可以利用原型模式简化对象的创建过程, 同时也能够提高效率
- 不用重新初始化对象, 而是动态地获取对象运行时的状态
- 如果原始对象发生变化(增减或减少属性), 其他克隆对象也会发生相应的变化, 无需修改代码
- 需要为每一个类配备一个克隆方法, 对于新创建的类来说是符合开闭原则(OCP), 但对于已有的类进行改造时, 需要修改源代码, 违背了开闭原则(OCP)
(二) 建造者模式
1. 建造者模式概述
建造者模式(Builder Pattern): 又叫生成器模式, 是一种对象构建模式. 它将一个复杂的对象的 表现 和 构建 分离. 即: 将一个复杂对象的创建过程抽象出来, 使用这个抽象过程的不同实现方式可以构造出不同表现的对象.
建造者模式是一步一步创建一个复杂的对象, 它允许用户只通过指定复杂对象的类型和内容就可以构建这个复杂的对象, 用户不需要知道内部的具体构建实现
比如: 超级跑车的定制, 用户只需要指定用 英国品牌的车架、日本品牌的轮胎、德国品牌的发动机等超跑的属性类型, 用户就可以得到想要的超跑, 而无需知道这些属性(车架、轮胎、发动机) 的 安装过程以及安装顺序, 这些安装操作会交给专业的人来处理
建造者模式中存在四个角色:
- Product(产品角色): 一个具体的产品对象(超级跑车)
- Builder(抽象的建造者): 创建以Product对象的各个部件指定的接口/抽象类
- ConcteteBuilder(具体的建造者): 实现接口, 构建和装配各个部件(指定各个品牌的车配件)
- Director(导演, 指挥者): 创建一个使用Builder接口的对象. 他主要是用户创建一个复杂的对象. 它主要有两个作用, 一是: 隔离客户与对象的生产过程; 二是: 负责控制产品对象的生产过程
注: 标准的建造者模式是存在4个角色的, 在一些特殊或简写建造者模式的情况下, Director导演角色可以跟 ConcteteBuilder具体的建造者角色合并. 这也不难理解, 具体的建造者完全可以具备了导演角色的两个作用
2. 建造者模式代码实现
Product产品角色: 超级跑车
public class SuperCar {
private String frames; // 车框架
private String tyre; // 轮胎
private String engine; // 发动机
// getter/setter/toString
}
Builder抽象的建造者: 创建以Product对象的各个部件指定的接口/抽象类
public abstract class SuperCarBuilder {
public abstract void builderFrames(String framesBrand); // 抽象方法: 构建车框架
public abstract void builderTyre(String tyreBrand); // 抽象方法: 构建轮胎
public abstract void builderEngine(String engineBrand); // 抽象方法: 构建发动机
public abstract SuperCar builderSuperCar(); // 抽象方法: 经过以上一系列的操作, 返回最终建造的产品
}
ConcteteBuilder具体的建造者: 保时捷生产商
public class PorscheSuperCarBuilder extends SuperCarBuilder{
private SuperCar superCar = new SuperCar();
// 保时捷生产商, 建造的超级跑车的三个步骤
@Override
public void builderFrames(String framesBrand) {
superCar.setFrames(framesBrand);
}
@Override
public void builderTyre(String tyreBrand) {
superCar.setTyre(tyreBrand);
}
@Override
public void builderEngine(String engineBrand) {
superCar.setEngine(engineBrand);
}
// 保时捷生产商, 经过以上三个步骤, 生产出保时捷超级跑车
@Override
public SuperCar builderSuperCar() {
return superCar;
}
}
Director导演: 负责控制产品对象的生产过程
public class SuperCarDirector {
private SuperCarBuilder superCarBuilder; // 抽象的建造者
public SuperCarDirector(SuperCarBuilder superCarBuilder) {
this.superCarBuilder = superCarBuilder;
}
// 由导演 去指挥 具体的建造者 建造超级跑车
public SuperCar ConstructSuperCar(String framesBrand, String tyreBrand, String engineBrand) {
superCarBuilder.builderFrames(framesBrand);
superCarBuilder.builderTyre(tyreBrand);
superCarBuilder.builderEngine(engineBrand);
return superCarBuilder.builderSuperCar();
}
}
Client客户端: 通过导演获取 Product产品对象
public class Client {
public static void main(String[] args) {
// 用户买保时捷超级跑车
PorscheSuperCarBuilder porscheSuperCarBuilder = new PorscheSuperCarBuilder();
// 超级跑车导演
SuperCarDirector superCarDirector = new SuperCarDirector(porscheSuperCarBuilder);
// 导演 控制产品对象的生产过程
SuperCar superCar = superCarDirector.ConstructSuperCar("英国", "日本", "德国");
System.out.println("保时捷超级跑车" + superCar);
}
}
3. 总结
建造者模式的注意事项和细节
- 客户端不必知道Product产品内部组成的细节, 将产品本身与产品创建过程解耦, 使得相同的创建过程可以创建不同的产品对象
- 每个具体的建造者都相对独立, 与其他建造者无关. 用户使用不同的建造者即可得到不同的产品对象
- 可以更加精细地控制产品的创建过程
- 增加新的具体的建造者无需修改原有的类库的代码, 只需继承抽象的建造者即可, 符合开闭原则
- 建造者模式使用于 要创建的产品一般具有较多的共同点, 其组成部分相似
4. 抽象工厂模式 vs 建造者模式
- 抽象工厂模式实现对产品家族(电子产品)的创建, 一个产品家族是一系列产品(电脑、手机): 具有不同分类维度的产品组合, 采用抽象工厂模式不需要关系构建过程, 只关心什么产品由什么工厂生产即可
- 抽象工厂模式: 是需要按照指定的蓝图构建产品, 它的主要目的是通过组件零配件而产生一个新产品