设计模式-创建型模式之原型、建造者设计模式

文章目录

七、原型模式

原型模式 (Prototype Pattern)是用于创建重复的对象,同时又能保证性能。它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口 ,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

例如:

创建一个可复制的支付类:

java 复制代码
public class WxPay implements Cloneable{
    public WxPay(){
        System.out.println("创建支付对象");
    }
    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        return (WxPay) super.clone();
    }
}

使用:

java 复制代码
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
    }
}

可以看到,创建了两个不同的对象,只进行了一次构件函数的执行,当直接创建对象的代价比较大时,就可以采用这种模式。

另外,原型的拷贝又有浅拷贝深拷贝 两个层次,上面的方式就是浅拷贝,只把当前对象做了拷贝,如果对象中有其他对象的引用,就不会进行拷贝,修改任意一个对象中的引用,对其他都会有影响。

比如上面的WxPay类做如下修改:

  1. 先定义其他操作类
java 复制代码
public class OtherOperation {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  1. 再修改WxPay类
java 复制代码
public class WxPay implements Cloneable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        return (WxPay) super.clone();
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }

}
  1. 演示
java 复制代码
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

上面可以看出,WxPay确实是复制出了一个实例,但是WxPay里面的OtherOperation实例没有复制,还只向同一个地址,导致只要修改任意一个对象中的OtherOperation对其他实例都会有影响。

上面已经看出默认是浅拷贝,但有的时候,我们又需要其中引用的对象也要为新实例,那怎么做呢,下面就来看下深拷贝的实现方式:

深拷贝实现方式 1:重写 clone 方法来实现深拷贝

深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)

实现方式 1,重写 clone 方法来实现深拷贝

  1. 修改 OtherOperation类
java 复制代码
public class OtherOperation implements Cloneable{
    private String name;

    public OtherOperation clone() throws CloneNotSupportedException {
        System.out.println("复制OtherOperation对象!");
        return (OtherOperation) super.clone();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  1. 修改WxPay,在clone中再复制OtherOperation ,以达到深拷贝的效果
java 复制代码
public class WxPay implements Cloneable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public WxPay clone() throws CloneNotSupportedException {
        System.out.println("复制对象!");
        WxPay wxPay = (WxPay) super.clone();
        wxPay.otherOperation = otherOperation.clone();
        return wxPay;
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }

}
  1. 演示
java 复制代码
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.clone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

可以看到,WxPay 和 OtherOperation 都是新实例。

深拷贝实现方式 2:通过对象序列化实现深拷贝

  1. OtherOperation 实现 Serializable
java 复制代码
public class OtherOperation implements  Serializable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  1. WxPay 实现Serializable,并提供方法对当前对象进行序列化操作
java 复制代码
public class WxPay implements Serializable {

    private OtherOperation otherOperation = new OtherOperation();

    public WxPay() {
        System.out.println("创建支付对象");
    }

    public OtherOperation getOperation() {
        return otherOperation;
    }

    public String getName() {
        return otherOperation.getName();
    }


    public WxPay 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);
            return (WxPay) ois.readObject();
        }
        catch (Exception e) {
            return null;
        }
        finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }
            catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}
  1. 效果
java 复制代码
public class demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        WxPay pay = new WxPay();
        WxPay pay1 = pay.deepClone();
        System.out.println(pay);
        System.out.println(pay1);
        System.out.println(pay.getOperation());
        System.out.println(pay1.getOperation());
        pay.getOperation().setName("abc");
        System.out.println(pay1.getOperation().getName());
    }
}

和上面我们手动的方式是一样的效果。

八、建造者模式

建造者模式 (Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

例如,在游戏场景中,需要构建人物模型,可能要先构建头部、再构建身体、然后四肢部分,最后才是一个完整的人物,如果都写在一起,肯定会导致代码复杂不易其他开发人员理解,对后期维护扩展难度都大,如果使用构建者模式,便可以将头部、身体、四肢,写在不同的人物实现里面,最后由一个构建类去按照一定的顺序加载它们,然后组成了一个更大的复杂的对象,这样便有利于维护扩展。

下面我们借助上面的例子简单用构建者模式实现下

  1. 定义人物对象,这里直接将头、身体、四肢简化为属性的形式表达。
java 复制代码
@Data
public class Person {
    private String head;
    private String body;
    private String foot;
}
  1. 定义人物构建接口,并定义具体的抽象方法。
java 复制代码
public interface PersonBuilder {
	//构建入口
	Person Builder();
	//构建头部
	void builderHead();
	//构建身体
	void builderBody();
	//构建四肢
	void builderFoot();
}
  1. 定义一个男孩人物的具体构建实现
java 复制代码
public class BoyBuilder implements PersonBuilder {
    private Person person;

    public BoyBuilder() {
        person = new Person();
    }

    @Override
    public Person Builder() {
        builderHead();
        builderBody();
        builderFoot();
        System.out.println("构建完成!");
        return person;
    }

    @Override
    public void builderHead() {
        System.out.println("开始构建男孩头部...");
        person.setHead("男孩头部信息");
    }

    @Override
    public void builderBody() {
        System.out.println("开始构建男孩身体...");
        person.setBody("男孩身体信息");
    }

    @Override
    public void builderFoot() {
        System.out.println("开始构建男孩四肢...");
        person.setFoot("男孩四肢部分信息");
    }
    
}
  1. 演示
java 复制代码
public class demo {

    public static void main(String[] args) {
        PersonBuilder personBuilder = new BoyBuilder();
        Person person = personBuilder.Builder();
        System.out.println(StringFormatter.concat("构建对象:", person.toString()).getValue());
    }
	
}

上面就是将一个人物的不同部分分别构建(实际中每个小的构建都应该在具体的实现中去表达,这里简化为了一个属性),一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

相关推荐
触底反弹2 小时前
🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚!
人工智能·设计模式·面试
杨充1 天前
10.可测试性实战设计
设计模式·开源·代码规范
杨充1 天前
9.重构十二式的实战
设计模式·开源·代码规范
杨充1 天前
6.设计原则的全景图
设计模式·开源·全栈
杨充1 天前
2.面向对象的特性
设计模式
杨充1 天前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充1 天前
8.反模式与坏味道
设计模式·开源·代码规范
杨充1 天前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法