设计模式之(六):原型设计模式

原型模式对应的内容概要

一.定义

原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

二.特点

不需要知道任何创建的细节,不调用构造函数

三.使用场景

  • 类初始化消耗较多资源
  • 类产生的一个对象需要非常繁琐的过程
  • 构造函数比较复杂(如属性比较多)
  • 循环体中生产大量相同类的对象时

四、优缺点

1.优点
  • 比直接new一个对象性能高
  • 简化创建过程
2.缺点
  • 必须配备clone方法
    • 实现Cloneable接口
    • 覆盖Object类的clone方法
  • 对克隆复杂对象或克隆出的对象进行复杂改造时,容易引入风险
  • 深拷贝、浅拷贝要运用得当
    • 深拷贝 : 对引用类型的数据类型的属性也进行拷贝,而不是指向同一块内存空间
    • 浅拷贝 : 只拷贝基本数据类型对应的属性

五.实例

1.浅拷贝实例

订单状态改变,发送短信的实例

  • 短信内容实体
  1. /**
  2. * 短信内容实体(实现Cloneable接口标识它是可克隆的)
  3. * @author shixinke
  4. */
  5. public class Sms implements Cloneable {
  6. /**
  7. * 短信ID
  8. */
  9. private Long id;
  10. /**
  11. * 接收者手机号
  12. */
  13. private String receiver;
  14. /**
  15. * 短信内容
  16. */
  17. private String message;
  18. /**
  19. * 短信模板
  20. */
  21. private String template;
  22. /**
  23. * 短信发送时间
  24. */
  25. private Integer createTime;
  26. public Sms() {
  27. }
  28. public Sms(Long id, String receiver, String message, String template) {
  29. this.id = id;
  30. this.receiver = receiver;
  31. this.message = message;
  32. this.template = template;
  33. this.createTime = Long.valueOf(System.currentTimeMillis() / 1000).intValue();
  34. }
  35. public void setId(Long id) {
  36. this.id = id;
  37. }
  38. public void setReceiver(String receiver) {
  39. this.receiver = receiver;
  40. }
  41. public void setMessage(String message) {
  42. this.message = message;
  43. }
  44. public void setTemplate(String template) {
  45. this.template = template;
  46. }
  47. public void setCreateTime(Integer createTime) {
  48. this.createTime = createTime;
  49. }
  50. public Long getId() {
  51. return id;
  52. }
  53. public String getReceiver() {
  54. return receiver;
  55. }
  56. public String getMessage() {
  57. return message;
  58. }
  59. public String getTemplate() {
  60. return template;
  61. }
  62. public Integer getCreateTime() {
  63. return createTime;
  64. }
  65. /*
  66. * 重写clone方法
  67. */
  68. public Object clone() {
  69. try {
  70. return super.clone();
  71. } catch (CloneNotSupportedException e) {
  72. System.out.println("克隆失败");
  73. return null;
  74. }
  75. }
  76. @Override
  77. public String toString() {
  78. return "Sms{" +
  79. "id=" + id +
  80. ", receiver='" + receiver + '\'' +
  81. ", message='" + message + '\'' +
  82. ", template='" + template + '\'' +
  83. ", createTime=" + createTime +
  84. '}';
  85. }
  86. }
  • 用户实体
  1. /**
  2. * 用户实体
  3. * @author shixinke
  4. */
  5. public class User {
  6. /**
  7. * 用户ID
  8. */
  9. private Long userId;
  10. /**
  11. * 用户昵称
  12. */
  13. private String nickname;
  14. /**
  15. * 用户手机号
  16. */
  17. private String mobile;
  18. public User(Long userId, String nickname, String mobile) {
  19. this.userId = userId;
  20. this.nickname = nickname;
  21. this.mobile = mobile;
  22. }
  23. public Long getUserId() {
  24. return userId;
  25. }
  26. public String getNickname() {
  27. return nickname;
  28. }
  29. public String getMobile() {
  30. return mobile;
  31. }
  32. }
  • 短信服务
  1. import java.util.List;
  2. /**
  3. * 发送短信服务
  4. * @author shixinke
  5. */
  6. public class SmsService {
  7. /**
  8. * 批量发送短信
  9. * @param userList
  10. * @param sms
  11. */
  12. public void send(List<User> userList, Sms sms) {
  13. for (User user : userList) {
  14. Sms temp = (Sms)sms.clone();
  15. if (temp != null) {
  16. temp.setReceiver(user.getMobile());
  17. temp.setId(sms.getId() + user.getUserId());
  18. temp.setMessage(String.format(temp.getTemplate(), user.getNickname()));
  19. System.out.println("发送短信:" + temp);
  20. }
  21. }
  22. System.out.println("原始短信对象:" + sms);
  23. }
  24. }
  • 测试代码
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. /**
  4. * 短信测试
  5. * @author shixinke
  6. */
  7. public class SmsTest {
  8. public static void main(String[] args) {
  9. Sms sms = new Sms(
  10. 1000L,
  11. null,
  12. null,
  13. "尊敬的%s,您的订单已开始发货,请耐心等候"
  14. );
  15. SmsService smsService = new SmsService();
  16. List<User> userList = new ArrayList<User>(5);
  17. userList.add(new User(1L, "张三", "158xxxx6666"));
  18. userList.add(new User(2L, "李四", "159xxxx6666"));
  19. userList.add(new User(3L, "王五", "150xxxx6666"));
  20. userList.add(new User(4L, "赵六", "130xxxx6666"));
  21. userList.add(new User(5L, "周七", "131xxxx6666"));
  22. smsService.send(userList, sms);
  23. }
  24. }

运行结果:

  1. 发送短信:Sms{id=1001, receiver='158xxxx6666', message='尊敬的张三,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
  2. 发送短信:Sms{id=1002, receiver='159xxxx6666', message='尊敬的李四,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
  3. 发送短信:Sms{id=1003, receiver='150xxxx6666', message='尊敬的王五,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
  4. 发送短信:Sms{id=1004, receiver='130xxxx6666', message='尊敬的赵六,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
  5. 发送短信:Sms{id=1005, receiver='131xxxx6666', message='尊敬的周七,您的订单已开始发货,请耐心等候', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}
  6. 原始短信对象:Sms{id=1000, receiver='null', message='null', template='尊敬的%s,您的订单已开始发货,请耐心等候', createTime=1570090084}

以上类的UML关系图:

原型模式UML关系图

2.深拷贝实例
(1)浅拷贝
  • 原型对象
  1. /**
  2. * 用户实体(原型对象)
  3. * @author shixinke
  4. */
  5. public class User implements Cloneable {
  6. /**
  7. * 用户ID
  8. */
  9. private Long userId;
  10. /**
  11. * 用户昵称
  12. */
  13. private String nickname;
  14. /**
  15. * 用户手机号
  16. */
  17. private String mobile;
  18. /**
  19. * 收货地址
  20. */
  21. private Address address;
  22. public User(Long userId, String nickname, String mobile, Address address) {
  23. this.userId = userId;
  24. this.nickname = nickname;
  25. this.mobile = mobile;
  26. this.address = address;
  27. }
  28. public Long getUserId() {
  29. return userId;
  30. }
  31. public void setUserId(Long userId) {
  32. this.userId = userId;
  33. }
  34. public String getNickname() {
  35. return nickname;
  36. }
  37. public void setNickname(String nickname) {
  38. this.nickname = nickname;
  39. }
  40. public String getMobile() {
  41. return mobile;
  42. }
  43. public void setMobile(String mobile) {
  44. this.mobile = mobile;
  45. }
  46. public Address getAddress() {
  47. return address;
  48. }
  49. public void setAddress(Address address) {
  50. this.address = address;
  51. }
  52. public Object clone() {
  53. try {
  54. return super.clone();
  55. } catch (CloneNotSupportedException e) {
  56. System.out.println("克隆失败");
  57. return null;
  58. }
  59. }
  60. }

注:address属性不是基本数据类型,在使用clone拷贝的时候是浅拷贝,克隆对象的address和原型对address是指向同一内存地址的

  • 引用类型对象
  1. /**
  2. * 用户收货地址
  3. * @author shixinke
  4. */
  5. public class Address {
  6. /**
  7. * 地址ID
  8. */
  9. private Long addressId;
  10. /**
  11. * 收货详细地址
  12. */
  13. private String address;
  14. public Address(Long addressId, String address) {
  15. this.addressId = addressId;
  16. this.address = address;
  17. }
  18. public Long getAddressId() {
  19. return addressId;
  20. }
  21. public void setAddressId(Long addressId) {
  22. this.addressId = addressId;
  23. }
  24. public String getAddress() {
  25. return address;
  26. }
  27. public void setAddress(String address) {
  28. this.address = address;
  29. }
  30. }
  • 测试代码:
  1. /**
  2. * 用户类测试
  3. * @author shixinke
  4. */
  5. public class UserTest {
  6. public static void main(String[] args) {
  7. Address address = new Address(100L, "浙江杭州");
  8. User user = new User(1L, "赵六", "15878451XXX", address);
  9. User temp = (User)user.clone();
  10. /**
  11. * 两个User对象(user和temp)不是同一个对象
  12. */
  13. System.out.println("user:" + user); //User@7adf9f5f
  14. System.out.println("temp:" + temp); //User@85ede7b
  15. System.out.println(user == temp); //false
  16. /**
  17. * user的address和temp的address是同一个对象
  18. */
  19. System.out.println("user.address:" + user.getAddress());//Address@5674cd4d
  20. System.out.println("temp.address:" + temp.getAddress());//Address@5674cd4d
  21. System.out.println(user.getAddress() == temp.getAddress()); //true
  22. }
  23. }
(2)实现深拷贝

对引用类型(非基本类型)的数据类型的对象进行单独的拷贝(clone)

  • Address类实现Cloneable接口,并重写clone方法
  1. /**
  2. * 用户收货地址
  3. * @author shixinke
  4. */
  5. public class Address implements Cloneable {
  6. /**
  7. * 地址ID
  8. */
  9. private Long addressId;
  10. /**
  11. * 收货详细地址
  12. */
  13. private String address;
  14. public Address(Long addressId, String address) {
  15. this.addressId = addressId;
  16. this.address = address;
  17. }
  18. public Long getAddressId() {
  19. return addressId;
  20. }
  21. public void setAddressId(Long addressId) {
  22. this.addressId = addressId;
  23. }
  24. public String getAddress() {
  25. return address;
  26. }
  27. public void setAddress(String address) {
  28. this.address = address;
  29. }
  30. /**
  31. * 重写clone方法
  32. * @return
  33. */
  34. public Object clone() {
  35. try {
  36. return super.clone();
  37. } catch (CloneNotSupportedException e) {
  38. e.printStackTrace();
  39. return null;
  40. }
  41. }
  42. }
  • User的clone方法,除了整体的clone,还要对非基本类型的数据进行克隆,即对address对象克隆
  1. /**
  2. * 用户实体(原型对象)
  3. * @author shixinke
  4. */
  5. public class User implements Cloneable {
  6. /**
  7. * 用户ID
  8. */
  9. private Long userId;
  10. /**
  11. * 用户昵称
  12. */
  13. private String nickname;
  14. /**
  15. * 用户手机号
  16. */
  17. private String mobile;
  18. /**
  19. * 收货地址
  20. */
  21. private Address address;
  22. public User(Long userId, String nickname, String mobile, Address address) {
  23. this.userId = userId;
  24. this.nickname = nickname;
  25. this.mobile = mobile;
  26. this.address = address;
  27. }
  28. public Long getUserId() {
  29. return userId;
  30. }
  31. public void setUserId(Long userId) {
  32. this.userId = userId;
  33. }
  34. public String getNickname() {
  35. return nickname;
  36. }
  37. public void setNickname(String nickname) {
  38. this.nickname = nickname;
  39. }
  40. public String getMobile() {
  41. return mobile;
  42. }
  43. public void setMobile(String mobile) {
  44. this.mobile = mobile;
  45. }
  46. public Address getAddress() {
  47. return address;
  48. }
  49. public void setAddress(Address address) {
  50. this.address = address;
  51. }
  52. public Object clone() {
  53. try {
  54. User user = (User)super.clone();
  55. //单独克隆address属性
  56. user.address = (Address)this.address.clone();
  57. return user;
  58. } catch (CloneNotSupportedException e) {
  59. System.out.println("克隆失败");
  60. return null;
  61. }
  62. }
  63. }
  • 再次测试
  1. user.address:Address@5674cd4d
  2. temp.address:Address@63961c42

此时两个对象的address属性不再指向同一地址,实现了深拷贝