设计模式之原型模式

1.简要概述

  • 原型模式很类似与现实世界中的克隆技术,就是以某个对象为原型,然后复制出新的对象,该对象具备原型对象的所有特点。
  • 原型模式用于创建重复的对象,同时又能保证性能,它提供了一种创建对象的最佳方式。
  • 原型模式创建对象的过程,就是使用原型实例指定要创建对象的类型,然后通过复制这个原型来创建新对象。
  • 原型模式的克隆方式与new方式有很大不同,采用new方式创建的对象属性值采用的是默认值,而克隆出的对象的属性值完全和原型对象相同,并且克隆出的对象发生改变不会影响原型对象。
  • 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。

2.模式结构

👉通常由一个原型接口( 负责定义创建所有原型实例的接口 ),多个具体的原型实现类( 负责实现对相应对象实例进行克隆的内部逻辑 ),一个客户类( 负责调用原型接口来创建指定的原型实例 )共同组成。

请添加图片描述

3.实现代码

举例 💡 :假设我们自定义一个原型接口,然后让多个具体的原型实体去实现这个接口,完成该原型实体的克隆操作。

原型接口

public interface MyPrototype {
    MyPrototype clone();
}

实体类1

public class Student implements MyPrototype {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    @Override
    public MyPrototype clone() {
        return new Student(name);
    }
}

实体类2

public class Teacher implements MyPrototype {

    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    @Override
    public MyPrototype clone() {
        return new Teacher(name);
    }
}

客户类

// 测试客户端
public class PrototypeClient{
    public static void main(String[] args) {
        // 使用原型模式创建学生实体
        Student stu = new Student("我是学生");
        Student stu1 = (Student) stu.clone();
        Student stu2 = (Student) stu.clone();
        
        System.out.println(stu1.getName()); // 我是学生
        System.out.println(stu2.getName()); // 我是学生
        System.out.println(stu1 == stu2); // false
     	
       	// 使用原型模式创建老师实体
        Teacher tea = new Student("我是老师");
        Teacher tea1 = (Teacher) tea.clone();
        Teacher tea2 = (Teacher) tea.clone();
        
        System.out.println(tea1.getName()); // 我是老师
        System.out.println(tea2.getName()); // 我是老师
        System.out.println(tea1 == tea2); // false
    }
}

4.优点好处

  • 在创建对象时,执行效率高,避免了重新执行构造的整个过程。
  • 不用重新初始化对象,而是动态的获得对象运行时的状态。
  • 如果原始对象内部发生变化,那么克隆对象也会跟着发生变化,不需要修改代码。

5.缺点弊端

如果我们系统中的实体类比较多时,我们则需要为每一个类都配备一个克隆方法,过程相对繁琐。

6.应用场景

  • 当通过new方式创建一个对象需要非常繁琐的过程的时候使用。
  • 当在短时间内需要创建大量的重复对象的时候使用。

7.应用示例

JDK源码中的Cloneable接口

public class Student implements Cloneable{
    
    ...
        
    @Override
    protected Object clone() throws CloneNotSupportedException {
        
        return super.clone();
    }
}

从上方代码可以看出,我们的Student类实现了Cloneable这个原型接口类,然后重写了clone()方法,实现了当前对象的克隆操作。

Spring框架中Bean对象的创建

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object stu1 = applicationContext.getBean("Student");
Object stu2 = applicationContext.getBean("Student");

System.out.println(stu1.toString());
System.out.println(stu2.toString());
System.out.println(stu1 == stu2); // false

当我们点击getBean()方法的实现,如下:

请添加图片描述

我们发现这里面有一个判断是否通过原型方式创建Bean实例的过程,其实这里面就用到了原型模式。

8.深克隆和浅克隆

  • 浅克隆:复制对象所有的变量都含有与原来对象相同的值,而所有的对象引用都仍然指向原来的对象引用。Cloneable接口中的clone方法默认就是浅克隆。

  • 深克隆:在浅克隆的基础上,深克隆把对象引用也创建了一份,而不是指向原来对象的引用。可以通过重写Cloneable接口中的clone方法,或者采用对象序列化的手段实现深克隆。

    待克隆的实体类

    public class Student{
        
        private String name;
            
        private Teacher teacher;
        
        public Student(String name, Teacher teacher){
            this.name = name;
            this.teacher = teacher;
        }
        
        // get和set方法
        ...
    }
    

    重写clone()方法进行实现

    public class Student implements Cloneable{
    
        @Override
        protected Object clone() {
            Object obj = null;
            
            try{
                obj = super.clone();
                
                Student stu = (Student) obj;
                stu.teacher = (Teacher) stu.teacher.clone();
                
                return stu;
            } catch(Exception e){
                //...
            }
        }
    }
    

    采用对象序列化手段进行实现

    public class Student implements Serializable{
    
        public Object clone() {
            Object obj = null;
            
            try{
                obj = super.clone();
                
                // 序列化
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(obj);
                
                // 反序列化
                byte[] bytes = bos.toByteArray();
    			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                ObjectIutputStream ois = new ObjectIutputStream(bis);
                Student stu = (Student) ois.readObject();
                
                return stu;
            } catch(Exception e){
                //...
            }
        }
    }
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小吉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值