栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

定义
  • 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新对象
  • 不需要知道任何创建细节,不调用构造函数
适用场景
  • 创建对象麻烦或困难时。1.对象种类繁多,无法整合到一个类时 2.要创建一个类,初始化时需要使用较多资源。
  • 想解耦框架和生成实例时,生成的框架不依赖于具体的类。
优点
  • 创建过程简单
  • 原型模式性能比直接new一个对象性能高
缺点
  • 必须配备克隆方法
  • 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入代码BUG
  • 必须清楚了解深拷贝与浅拷贝
原型模式中的角色 角色说明
  • Prototype(原型)

    • Product角色负责定义用于复制现有实例来生成新实例的方法。在实例程序中,由Cloneable接口来扮演此角色。
  • ConcreteProrotype(具体原型)

    • ConcretePrototype角色负责实现复制现有实例并生成新实例方法。在实例程序中,由User类扮演此角色。
  • Client(使用者)

    • Client角色负责使用复制实例的方法生成新的实例。在实例程序中,由单元测试类扮演此角色。
类图

代码实现 原型模式代码 写法一

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,这里就引发深拷贝与浅拷贝的问题。

深拷贝 VS 浅拷贝

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

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

深拷贝代码改造 重新改造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了。

总结

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

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号