专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

设计模式(一)-创建型之原型模式

定义

  • 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新对象
  • 不需要知道任何创建细节,不调用构造函数

适用场景

  • 创建对象麻烦或困难时。1.对象种类繁多,无法整合到一个类时 2.要创建一个类,初始化时需要使用较多资源。
  • 想解耦框架和生成实例时,生成的框架不依赖于具体的类。

优点

  • 创建过程简单
  • 原型模式性能比直接new一个对象性能高

缺点

  • 必须配备克隆方法
  • 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入代码BUG
  • 必须清楚了解深拷贝与浅拷贝

原型模式中的角色

角色说明

  • Prototype(原型)
    • Product角色负责定义用于复制现有实例来生成新实例的方法。在实例程序中,由Cloneable接口来扮演此角色。
  • ConcreteProrotype(具体原型)
    • ConcretePrototype角色负责实现复制现有实例并生成新实例方法。在实例程序中,由User类扮演此角色。
  • Client(使用者)
    • Client角色负责使用复制实例的方法生成新的实例。在实例程序中,由单元测试类扮演此角色。

类图

73_1.png

代码实现

原型模式代码

写法一

1、用java实现原型模式比较简单,只需要将目标类实现Cloneable接口即可

@Getter
@Setter
@AllArgsConstructor
public class User implements Cloneable {
    private String name;
    private Date birthday;

    public User() {
        System.out.println("User constructor被调用");
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("User clone被调用");
        // 浅克隆写法
        return super.clone();
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

写法二

1、 抽象父类实现Cloneable接口

public abstract class AbstractUser implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("抽象父类clone方法被调用");
        return super.clone();
    }
}

1、 目标类继承抽象父类接口

@Getter
@Setter
public class User extends AbstractUser {
    private String name;
    private Date birthday;

    public User() {
        System.out.println("User constructor被调用");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

单元测试(写法一)

测试代码

    @Test
    public void testClone() throws CloneNotSupportedException {
        Date birthday = new Date(0L);
        // 1. 创建用户对象
        User user1 = new User();
        user1.setName("小林");
        user1.setBirthday(birthday);

        // 2. 克隆用户对象
        User user2 = (User) user1.clone();
        // 3. user1,user2对比
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user1 == user2);

        System.out.println("<==============我是分割线============>");

        // 4. 赋值新的birthday并打印
        user1.getBirthday().setTime(666666666666L);
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user1 == user2);
    }

执行结果

根据user1和user2的hash值不同可以看出是不同的类,但user1的birthday修改后,user2的birthday也同样被修改,说明user1和user2指向了同一个birthday,这里就引发深拷贝与浅拷贝的问题。

73_2.png

深拷贝 VS 浅拷贝

使用clone(),之后,由于birthday是一个引用对象,由于是浅拷贝,user中的引用成员变量依然指向的是同一个。

73_3.png

什么是深拷贝,我们cloneuser对象的同时,也将其内部引用的对象进行拷贝,使得每个引用对象无关联,都是单独的对象。

73_4.png

深拷贝代码改造

重新改造user中clone()方法的实现

@Getter
@Setter
public class User implements Cloneable {
    private String name;
    private Date birthday;

    public User() {
        System.out.println("User constructor被调用");
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("User clone被调用");
        // 浅拷贝写法
        // return super.clone();

        // 深拷贝写法
        User user = (User) super.clone();
        user.birthday=(Date) user.getBirthday().clone();
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

执行结果

可以看到改造完成之后user1的birthday与user2的birthday指向不是同一个birthday了。

73_5.png

总结

使用原型模式时要十分注意深拷贝、浅拷贝的问题,即使了解了深拷贝和浅拷贝,在写代码的过程中一个疏忽就可能产生BUG,如果对深拷贝和浅拷贝不了解同学,需要慎用原型模式。

文章永久链接:https://tech.souyunku.com/26487

未经允许不得转载:搜云库技术团队 » 设计模式(一)-创建型之原型模式

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们