Java-第十八部分-设计模式-原型模式和建造者模式

326 阅读6分钟

设计模式全文

原型模式

  • 有一只羊,名tom,年龄1,颜色白,创建10只完全一样的羊
  • prototype,用原型实例指定创建对象的类型,通过拷贝这些原型,创建新的对象,是一种创建型设计模式,允许一个对象在创建另一个可定制的对象
  • 原理,通过将一个原型对象传给要发动创建的对象,这个发动创建的对象,通过请求原型对象拷贝它们自己来实施创建对象

传统思路

demo.png

  • 直接重复创建
Sheep sheep = new Sheep("tom", "白色", 1);
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge());
  • 缺点
  1. 创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂,效率较低
  2. 总是需要重新初始化对象,不能动态地获取对象运行时的状态
  • 改进,java中Object类是所有类的根类,其中clone()方法,可以将java对象复制一遍,但是需要实现Cloneable接口,让该类能够复制且具有复制的能力

实现Cloneable接口

  • 为对象增加属性时,无需修改复制对象的代码
  • 默认浅拷贝
public class Sheep implements Cloneable {
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sheep;
    }
}
  • 使用
Sheep sheep = new Sheep("tom","white",1);
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();

spring中的原型模式

  • 原型模式来创建bean
<bean id="id01" class="com.java.spring.bean.Monster" scope="prototype"/>
  • 获取bean
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// 获取monster[通过id获取monster]
Object bean = applicationContext.getBean("id01");
System.out.println("bean" + bean); // 输出 "牛魔王" ....
Object bean2 = applicationContext.getBean("id01");
System.out.println("bean2" + bean2); //输出 "牛魔王" .....
System.out.println(bean == bean2); // false
  • getBean
public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}
  • 调用核心方法doGetBean
public Object getBean(String name) throws BeansException {
    return this.doGetBean(name, (Class)null, (Object[])null, false);
}
  • 检查是以什么方式创建 image.png
  • mbd image.png
  • 调用createBean image.png

浅拷贝和深拷贝

  • 如果属性是对象,在克隆时,没有真正被复制,只是复制了引用
Sheep sheep = new Sheep("tom","white",1);
sheep.friend = new Sheep("jack","black",2);
Sheep sheep1 = (Sheep) sheep.clone();
System.out.println(sheep.friend.hashCode()); //1265094477
System.out.println(sheep1.friend.hashCode()); //1265094477

浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,将属性值复制一份
  • 对于数据类型是引用数据类型的成员变量,数组、类的对象,浅拷贝会进行引用传递,只是将该成员变量的引用值(内存地址)复制一份,实际上两个对象的引用数据类型的成员变量都指向同一块区域,一变均变

深拷贝

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象
  • 对象进行深拷贝,要对整个对象进行拷贝
  • 实现方式
  1. 重写clone
  2. 对象序列化

重写clone实现

  • DeepCloneableTarget 也需要实现clone,克隆其中的成员变量
@Override
protected Object clone() throws CloneNotSupportedException {
    Object deep = null;
    //完成对基本数据类型和String的克隆
    deep = super.clone();
    //对引用类型进行处理
    DeepProtoType deepProtoType = (DeepProtoType) deep;
    deepProtoType.dct = (DeepCloneableTarget) dct.clone();
    return deepProtoType;
}

序列化

//序列化
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);
        //反序列化
        bis = new ByteArrayInputStream(bos.toByteArray());
        ois = new ObjectInputStream(bis);
        DeepProtoType dpt = (DeepProtoType) ois.readObject();
        return dpt;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } finally {
        try {
            if (bos != null) bos.close();
            if (oos != null) oos.close();
            if (bis != null) bis.close();
            if (ois != null) ois.close();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}

小结

  • 优点
  1. 简化复制对象的创建过程,不用重新初始化对象,动态获取对象运行时的状态
  2. 如果原始对象发生变化,其他克隆对象也会发生相应的变化,无需修改代码
  • 缺点
  1. 深拷贝需要比较复杂的处理,分别处理引用数据类型
  2. 原型模式需要为每个类,配备一个克隆方法,对已有的类进行改造,需要修改其源代码,违反了ocp原则

建造者模式

  • 需要建房子,打桩、砌墙、封顶
  • 房子有各式各样,过程一样,但是要求不同
  • Builder Pattern,生成器模式,对象构建模式
  • 将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同属性的对象
  • 四个角色
  1. product 产品角色,具体的产品对象
  2. Builder 抽象建造者,创建一个product对象的各个部件指定的接口/抽象类
  3. ConcreteBuilder 具体建造者,实现接口,构建和装配各个部件
  4. Director 指挥者,一个使用builder接口的对象,用于创建一个复杂的对象。一是,隔离了客户和对象的生产过程;二是,负责控制产品对象的生产过程

传统思维

demo.png

  • 没有设计缓存层,将产品和创建产品的过程封装在一起了,耦合性增强
  • 将产品和产品建造过程进行解耦

改进

  • 建造者类图 demo.png
  • 以造房子为例 demo2.png
  • 产品,规范化,不同的产品区别在于属性值不同
public class House {
    private String basic;
    private String wall;
    private String roofed;
}
  • HouseBuilder,规范制造的部分,具体实现交给子类,都需要建造
public abstract class HouseBuilder {
    protected House house = new House();
    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void roofed();
    //建造
    public House build() {
        return house;
    }
}
  • CommonHouse,负责具体实现
public class CommonHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        house.setBasic("common");
        System.out.println("CommonHouse buildBasic");
    }
    @Override
    public void buildWalls() {
        house.setWall("common");
        System.out.println("CommonHouse buildWalls");
    }
    @Override
    public void roofed() {
        house.setRoofed("common");
        System.out.println("CommonHouse roofed");
    }
}
  • HouseDirector,指挥者,不同的指挥者,过程顺序不同
public class HouseDirector {
    HouseBuilder hb = null;
    //构造器传入
    public HouseDirector(HouseBuilder hb) {
        this.hb = hb;
    }
    //setter传入
    public void setHb(HouseBuilder hb) {
        this.hb = hb;
    }
    //处理建房流程
    public House contructHouse() {
        hb.buildBasic();
        hb.buildWalls();
        hb.roofed();
        return hb.build();
    }
}

StringBuilder源码

  • 产品是String或者Buffer
  • StringBuilder继承AbstractStringBuilder,既充当了指挥者,又充当了建造者,建造方法的实现是由AbstractStringBuilder完成的 image.png
  • AbstractStringBuilder实现Appendable的接口方法,实现了多个append方法,作为建造者,只是不能实例化,被不同的指挥者调用 image.png
  • Appendable定义了多个append方法,作为抽象建造者 image.png

小结

  • 每一个具体建造者都相对独立,与其他建造者无关,用户使用不同的具体建造者,得到不同的产品
  • 更加精细化地控制产品的创建过程
  • 新增加的具体建造者无须修改原有类库代码,符合开闭原则
  • 创建的产品需要有较多的共同点,其组成部分相似
  • 抽象工厂模式和建造者模式
  1. 抽象工厂模式,实现对产品家族的创建,是一系列的产品,具有不同分类维度的产品组合,不需要关心构建过程,只关系什么产品通过什么工厂生产
  2. 建造者模式,要求按照指定的蓝图建造产品,通过组装零配件而产生一个新产品