设计模式学习笔记(七)原型模式

概念

原型模式(Prototype Pattern)是一种创建型设计模式,其目的是通过复制现有对象来创建新对象,而不是通过实例化类。它允许我们通过使用原型对象作为蓝本来生成新的对象,从而避免了直接创建对象时所需的复杂初始化过程。
在原型模式中,我们首先创建一个原型对象,然后通过克隆该原型对象来创建新对象。这个克隆可以是浅克隆(Shallow Clone)或深克隆(Deep Clone)。

在这里插入图片描述

基本使用

示例一

手动编写克隆方法:

/**
 * 原型抽象类
 * */
public interface Prototype {
    String  attr = null; //成员属性
    public String getAttr();
    public void setAttr(String attr);
    public Prototype clone();
}

/**
 * 原型具体实现类
 * */
public class ConcretePrototype implements Prototype {

    private String  attr; //成员属性

    @Override
    public String getAttr() {
        return attr;
    }

    @Override
    public void setAttr(String attr) {
        this.attr = attr;
    }

    @Override
    public Prototype clone() {
        Prototype prototype = new ConcretePrototype();
        prototype.setAttr(this.attr);
        return prototype;
    }
}
//客户端
 Prototype prototype = new ConcretePrototype();
        prototype.setAttr("123");
        Prototype prototype1 = prototype.clone();
        System.out.println(prototype == prototype1);//false
        System.out.println(prototype1.getAttr());//123

示例二

基于Cloneable接口实现clone

/**
 * 基于Cloneable接口实现
 * */
public class ConcretePrototype1 implements Cloneable {
    private String  attr; //成员属性
    public String getAttr() {
        return attr;
    }

    public void setAttr(String attr) {
        this.attr = attr;
    }
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }
}

//客户端
Prototype prototype2 = new ConcretePrototype();
        prototype2.setAttr("456");
        Prototype prototype3 = prototype2.clone();
        System.out.println(prototype2 == prototype3);//false
        System.out.println(prototype3.getAttr());//456

浅克隆与深克隆

浅克隆:在浅克隆中,对于对象的字段,只有对象的引用被复制,而不复制引用对象本身。这意味着原型对象和克隆对象共享相同的引用对象,如果其中一个对象修改了引用对象,另一个对象也会受到影响。简单来说,如果是值类型则将值复制一份,如果是引用类型,则复制该成员变量的地址。

示例一

我们定义一个子类

public class Child {
    String name;
    int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

将Child子类作为ConcretePrototype1类的成员变量

/**
 * 基于Cloneable接口实现(浅拷贝)
 * */
public class ConcretePrototype1 implements Cloneable {

    private String  attr; //成员属性
    private Child child;
    public String getAttr() {
        return attr;
    }
    public void setAttr(String attr) {
        this.attr = attr;
    }
    public Child getChild() {
        return child;
    }
    public void setChild(Child child) {
        this.child = child;
    }
//浅拷贝
    @Override
    public ConcretePrototype1 clone() throws CloneNotSupportedException {
        return (ConcretePrototype1) super.clone();
    }
}

客户端测试克隆之后,子类是否为同一个对象地址

ConcretePrototype1 concretePrototype1,concretePrototype1New;

        Child child = new Child();
        concretePrototype1 = new ConcretePrototype1();//创建原型对象
        concretePrototype1.setChild(child);
        concretePrototype1New = concretePrototype1.clone();//浅拷贝原型对象

        System.out.println(concretePrototype1.getChild() == concretePrototype1New.getChild());	//true

结果原型与拷贝之后的子类为同一个对象。

深克隆:在深克隆中,除了复制对象的引用,还会复制引用对象本身。这意味着即使一个对象修改了引用对象,另一个对象也不会受到影响。简而言之,除了值类型的变量被复制了一份之外,引用类型的值也会被复制一份,而不是共享同一个地址
实现方法:我们需要实现**(序列化)Serializable接口,序列化实际上就是将对象写到流中的过程,在这一过程中,不仅该对象会被拷贝一份到流中,其子类也会被拷贝一同写进流中,而原对象还在内存中,数据流中的对象及其子类相当于被拷贝了一份。在反序列化的过程中,会重新创建一个全新的对象,并将原始对象的属性值逐个复制到新对象中。
这种方式可以实现深拷贝的原因是它会对对象进行
递归复制**,包括对象的属性、引用对象以及引用对象的属性等等。当序列化后再反序列化时,每个对象都会被单独创建,这样就避免了对象之间共享引用的问题。

示例二

我们定义一个子类,实现Serializable接口

public class Child implements Serializable {
    String name;
    int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

将Child子类作为ConcretePrototype1类的成员变量,同样也实现Serializable接口

/**
 * 通过序列化的方式读入流中 实现深拷贝
 * */
public class ConcretePrototype1 implements Serializable {
    private String  attr; //成员属性
    private Child child;

    public String getAttr() {
        return attr;
    }
    public void setAttr(String attr) {
        this.attr = attr;
    }
    public Child getChild() {
        return child;
    }
    public void setChild(Child child) {
        this.child = child;
    }
    //深拷贝
    public ConcretePrototype1 deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);

        ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bai);
        return (ConcretePrototype1) ois.readObject();
    }
}

客户端测试克隆之后,子类是否为同一个对象地址

ConcretePrototype1 concretePrototype1,concretePrototype1New;

        Child child = new Child();
        concretePrototype1 = new ConcretePrototype1();//创建原型对象
        concretePrototype1.setChild(child);
        concretePrototype1New = concretePrototype1.deepClone();//深拷贝原型对象

        System.out.println(concretePrototype1.getChild() == concretePrototype1New.getChild());	//false

结果原型与拷贝之后的子类为同一个对象。

原型管理器

原型管理器(Prototype Manager)是在原型模式中使用的一个重要组件。它负责管理和存储原型对象,客户端通过原型管理器获取需要的原型对象,然后进行克隆来创建新的对象。
原型管理器通常使用哈希表(Hash Table)或者其他数据结构来存储原型对象,以便能够方便地根据键值对来快速获取对应的原型对象。原型管理器还可以提供一些方法,用于注册、检索和删除原型对象。
使用原型管理器可以带来以下好处:
隐藏了原型对象的具体创建细节,客户端只需通过管理器获取对应的原型对象即可。
提高了对象的复用性和创建效率。通过原型管理器,可以在需要时直接克隆现有的对象,而不需要每次都重新实例化和初始化对象。
方便了添加、修改和删除原型对象。原型管理器集中管理了所有原型对象,可以灵活地进行原型对象的管理和维护。
下面是一个简单示例,展示如何实现一个原型管理器:

1、原型模板接口

public interface ProjectPattern extends Cloneable {
    public ProjectPattern clone();
    public void build();
}

2、原型实现类

public class Code1Project implements ProjectPattern{
    @Override
    public ProjectPattern clone() {
        ProjectPattern project = null;
        try {
            project = (ProjectPattern) super.clone();
        }catch (CloneNotSupportedException  c) {
            System.out.println("无法赋值");
        }
        return project;
    }

    @Override
    public void build() {
        System.out.println("构建代码1工程。。。");
    }
}


public class Code2Project implements ProjectPattern{
    @Override
    public ProjectPattern clone() {
        ProjectPattern project = null;
        try {
            project = (ProjectPattern) super.clone();
        }catch (CloneNotSupportedException  c) {
            System.out.println("无法赋值");
        }
        return project;
    }

    @Override
    public void build() {
        System.out.println("构建代码2工程。。。");
    }
}

3、原型管理器

/**
 * 原型管理器
 * 使用单例饿汉式实现
 * */
public class ProjectManager {
    private Hashtable ht = new Hashtable();
    private static ProjectManager pm = new ProjectManager();

    //初始化模板
    private ProjectManager() {
    //若想对Code1Project或Code2Project的成员变量初始化值,可以在这实现,然后在放入ht中
        ht.put("code1",new Code1Project());
        ht.put("code2",new Code2Project());
    }
    //获取到单例模式下唯一的原型管理器对象
    public static ProjectManager getProjectManager() {
        return pm;
    }

    //增加原型的可扩展性
    public void addProject(String key, ProjectPattern project) {
        ht.put(key, project);
    }

    //通过各具体实现类的克隆方法实现浅拷贝
    public ProjectPattern getProjectByClone(String key) {
        return ((ProjectPattern) (ht.get(key))).clone();
    }
}

4、客户端

        //获取到单例模式下唯一的原型管理器对象,此时此对象已经初始化完成
        ProjectManager projectManager = ProjectManager.getProjectManager();

        ProjectPattern code1,code2,code3,code4;
        //验证实体类是否复制成功
        code1 = projectManager.getProjectByClone("code1");
        code1.build();
        code2 = projectManager.getProjectByClone("code1");
        code2.build();

        //验证是否为同一个对象
        code3 = projectManager.getProjectByClone("code2");
        code4 = projectManager.getProjectByClone("code2");
        System.out.println(code3 == code4);

总结

优点:
1、减少了对象的创建成本:原型模式通过克隆现有的对象来创建新的对象,避免了重复的初始化过程,提高了创建对象的效率。
2、提高了对象的复用性:通过原型模式可以方便地复制已有对象的状态,从而创建新的对象。这样可以避免创建大量相似对象的开销。
3、隐藏了对象创建的细节:客户端只需通过克隆方法即可获取新的对象,无需关心对象的具体实现和创建细节。

缺点:
1、深度克隆可能较为复杂:如果原型对象中包含引用类型的属性,并且需要进行深度克隆,那么克隆过程可能比较复杂。需要注意在克隆过程中处理好关联对象的克隆。
2、必须实现 Cloneable 接口:为了使用原型模式,原型类必须实现 Cloneable 接口并重写 clone 方法。这对一些不支持克隆或者不可修改的类是一种限制。

适用环境:
1、当对象的创建过程比较复杂且耗时时,可以使用原型模式来节省资源。
2、当需要创建大量相似对象时,可以使用原型模式来提高创建效率。
3、当希望对对象的某个部分进行修改而不影响其他部分时,可以使用原型模式来克隆一个对象然后进行修改。
4、当类中多层嵌套着类的时候,为了免去每层对象单独创建然后再赋值的麻烦,直接使用克隆即可。

总之,原型模式适用于创建成本较高、对象结构相对稳定且需要频繁创建或者修改的场景。它通过克隆已有的对象来创建新对象,从而提高了创建效率和对象的复用性。但是需要注意处理关联对象的克隆和实现 Cloneable 接口的限制。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值