设计模式:03-原型模式 / 建造者模式(生成器模式)


本篇博客主要是学习 韩顺平_Java设计模式 做一个学习笔记使用

5. 原型模式

基本介绍
1) 原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2) 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
3) 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它 们自己来实施创建,即 对象.clone()
4) 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣

5.1. 问题的引入

  1. /**
  2. * @author houyu
  3. * @createTime 2019/11/18 20:38
  4. */
  5. public class Demo1 {
  6. /**
  7. * 定义个类 Person
  8. */
  9. public static class Person {
  10. private int age;
  11. private String name;
  12. public Person(int age, String name) {
  13. this.age = age;
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public String getName() {
  20. return name;
  21. }
  22. @Override
  23. public String toString() {
  24. final StringBuilder sb = new StringBuilder("Person{");
  25. sb.append("age=").append(age);
  26. sb.append(", name='").append(name).append('\'');
  27. sb.append('}');
  28. return sb.toString();
  29. }
  30. }
  31. public static void main(String[] args) {
  32. // 问题的引入:
  33. // 需要拷贝 Person 5份
  34. Person original = new Person(10, "tom");
  35. //
  36. Person person1 = new Person(original.getAge(), original.getName());
  37. Person person2 = new Person(original.getAge(), original.getName());
  38. Person person3 = new Person(original.getAge(), original.getName());
  39. Person person4 = new Person(original.getAge(), original.getName());
  40. Person person5 = new Person(original.getAge(), original.getName());
  41. //
  42. System.out.println(person1);
  43. System.out.println(person2);
  44. System.out.println(person3);
  45. System.out.println(person4);
  46. System.out.println(person5);
  47. /**
  48. * 分析问题:
  49. * 传统的方式的优缺点
  50. *
  51. * 1) 优点是比较好理解,简单易操作。
  52. * 2) 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
  53. * 3) 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
  54. * 4) 改进的思路分析
  55. */
  56. }
  57. }

5.2 原型模式的浅拷贝

使用默认的原型模式实现的是对象的浅拷贝
什么是浅拷贝?
这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会> 进行值传递
如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]…)

  1. 实现接口 implements Cloneable
  2. 重写方法: @Override protected Object clone(){}
  3. 调用端调用clone()即可

代码

  1. import java.util.Arrays;
  2. /**
  3. * @author houyu
  4. * @createTime 2019/11/18 20:38
  5. */
  6. public class Demo2 {
  7. /**
  8. * 定义个类 Person
  9. */
  10. public static class Person implements Cloneable {
  11. private int age;
  12. private String name;
  13. private int[] array = {1, 2};
  14. public Person(int age, String name) {
  15. this.age = age;
  16. this.name = name;
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public int[] getArray() {
  25. return array;
  26. }
  27. @Override
  28. public String toString() {
  29. final StringBuilder sb = new StringBuilder("Person{");
  30. sb.append("age=").append(age);
  31. sb.append(", name='").append(name).append('\'');
  32. sb.append(", array=").append(Arrays.toString(array));
  33. sb.append('}');
  34. return sb.toString();
  35. }
  36. @Override
  37. protected Object clone() {
  38. try {
  39. return super.clone();
  40. } catch(CloneNotSupportedException e) {
  41. e.printStackTrace();
  42. }
  43. return null;
  44. }
  45. }
  46. public static void main(String[] args) {
  47. // 问题的引入:
  48. // 需要拷贝 Person 5份
  49. Person original = new Person(10, "tom");
  50. //
  51. Person person1 = (Person) original.clone();
  52. Person person2 = (Person) original.clone();
  53. Person person3 = (Person) original.clone();
  54. Person person4 = (Person) original.clone();
  55. Person person5 = (Person) original.clone();
  56. //
  57. System.out.println(person1 == original);
  58. System.out.println(person2 == original);
  59. System.out.println(person3.getArray() == original.getArray());
  60. System.out.println(person4);
  61. System.out.println(person5);
  62. /*
  63. * false
  64. * false
  65. * true
  66. * Person{age=10, name='tom', array=[1, 2]}
  67. * Person{age=10, name='tom', array=[1, 2]}
  68. */
  69. //
  70. /**
  71. * 注意点:
  72. *
  73. * 1. 使用默认的原型模式实现的是对象的浅拷贝
  74. *
  75. * 什么是浅拷贝?
  76. * 这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会进行值传递
  77. *
  78. * 如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]...)
  79. *
  80. */
  81. }
  82. }

5.3 重写默认原型模式实现深拷贝

注意点:

  • 使用默认的原型模式close,会导致引用类型没有深拷贝,引用是同一个,因此可能会导致其他问题,如:线程安全等。。。
  • 改进方案1(不推荐):在原来的clone基础之上,手动一个一个添加引用类型的clone
  • 改进方案2(推荐):使用流写入写出进行克隆(ObjectOutputStream,ObjectInputStream)
  • 改进方案3(推荐):使用JSON序列化工具进行克隆(fastjson,gson,jackson…)
  1. 实现接口 implements Cloneable, Serializable
  2. 重写方法: @Override protected Object clone(){},使用 ObjectOutputStream ObjectInputStream 实现
  3. 调用端调用clone()即可
  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.Closeable;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. import java.io.Serializable;
  8. import java.util.Arrays;
  9. /**
  10. * @author houyu
  11. * @createTime 2019/11/18 20:38
  12. */
  13. public class Demo3 {
  14. /**
  15. * 定义个类 Person
  16. */
  17. public static class Person implements Cloneable, Serializable {
  18. private int age;
  19. private String name;
  20. private int[] array = {1, 2};
  21. public Person(int age, String name) {
  22. this.age = age;
  23. this.name = name;
  24. }
  25. public int getAge() {
  26. return age;
  27. }
  28. public String getName() {
  29. return name;
  30. }
  31. public int[] getArray() {
  32. return array;
  33. }
  34. @Override
  35. public String toString() {
  36. final StringBuilder sb = new StringBuilder("Person{");
  37. sb.append("age=").append(age);
  38. sb.append(", name='").append(name).append('\'');
  39. sb.append(", array=").append(Arrays.toString(array));
  40. sb.append('}');
  41. return sb.toString();
  42. }
  43. @Override
  44. protected Object clone() {
  45. ObjectOutputStream objectOutputStream = null;
  46. ByteArrayOutputStream byteArrayOutputStream = null;
  47. ObjectInputStream objectInputStream = null;
  48. ByteArrayInputStream byteArrayInputStream = null;
  49. try {
  50. byteArrayOutputStream = new ByteArrayOutputStream();
  51. objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
  52. objectOutputStream.writeObject(this);
  53. byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
  54. objectInputStream = new ObjectInputStream(byteArrayInputStream);
  55. return objectInputStream.readObject();
  56. } catch(IOException | ClassNotFoundException e) {
  57. e.printStackTrace();
  58. } finally {
  59. close(objectOutputStream, byteArrayOutputStream, objectInputStream, byteArrayInputStream);
  60. }
  61. return null;
  62. }
  63. private void close(Closeable... closeables) {
  64. for(Closeable closeable : closeables) {
  65. if(closeable == null) {
  66. continue;
  67. }
  68. try { closeable.close(); } catch(IOException e) {}
  69. }
  70. }
  71. }
  72. public static void main(String[] args) {
  73. // 问题的引入:
  74. // 需要拷贝 Person 5份
  75. Person original = new Person(10, "tom");
  76. //
  77. Person person1 = (Person) original.clone();
  78. Person person2 = (Person) original.clone();
  79. Person person3 = (Person) original.clone();
  80. Person person4 = (Person) original.clone();
  81. Person person5 = (Person) original.clone();
  82. //
  83. System.out.println(person1 == original);
  84. System.out.println(person2 == original);
  85. System.out.println(person3.getArray() == original.getArray());
  86. System.out.println(person4);
  87. System.out.println(person5);
  88. /*
  89. * false
  90. * false
  91. * true
  92. * Person{age=10, name='tom', array=[1, 2]}
  93. * Person{age=10, name='tom', array=[1, 2]}
  94. */
  95. //
  96. /**
  97. * 注意点:
  98. *
  99. * 1. 必须需要实现Serializable
  100. *
  101. */
  102. }
  103. }

5.4 原型模式在 Spring 框架中源码分析(getBean)

  1. // 1.0 org.springframework.context.support.AbstractApplicationContext
  2. public Object getBean(String name) throws BeansException {
  3. assertBeanFactoryActive();
  4. return getBeanFactory().getBean(name);
  5. }
  6. // 2.0 org.springframework.beans.factory.support.AbstractBeanFactory
  7. public Object getBean(String name) throws BeansException {
  8. return doGetBean(name, null, null, false);
  9. }
  10. // 3.0 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean()
  11. // Create bean instance.
  12. if (mbd.isSingleton()) {
  13. sharedInstance = getSingleton(beanName, () -> {
  14. try {
  15. return createBean(beanName, mbd, args);
  16. }
  17. catch (BeansException ex) {
  18. // Explicitly remove instance from singleton cache: It might have been put there
  19. // eagerly by the creation process, to allow for circular reference resolution.
  20. // Also remove any beans that received a temporary reference to the bean.
  21. destroySingleton(beanName);
  22. throw ex;
  23. }
  24. });
  25. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  26. }
  27. else if (mbd.isPrototype()) {
  28. // It's a prototype -> create a new instance.
  29. Object prototypeInstance = null;
  30. try {
  31. beforePrototypeCreation(beanName);
  32. prototypeInstance = createBean(beanName, mbd, args);
  33. }
  34. finally {
  35. afterPrototypeCreation(beanName);
  36. }
  37. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  38. }
  39. else {
  40. String scopeName = mbd.getScope();
  41. final Scope scope = this.scopes.get(scopeName);
  42. if (scope == null) {
  43. throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
  44. }
  45. try {
  46. Object scopedInstance = scope.get(beanName, () -> {
  47. beforePrototypeCreation(beanName);
  48. try {
  49. return createBean(beanName, mbd, args);
  50. }
  51. finally {
  52. afterPrototypeCreation(beanName);
  53. }
  54. });
  55. bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
  56. }
  57. catch (IllegalStateException ex) {
  58. throw new BeanCreationException(beanName,
  59. "Scope '" + scopeName + "' is not active for the current thread; consider " +
  60. "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
  61. ex);
  62. }
  63. }

6. 建造者模式(生成器模式)

基本介绍
1) 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出 来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
2) 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

6.1 建造者模式的四个角色

1) Product(产品角色): 一个具体的产品对象。
2) Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
3) ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
4) Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作 用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

建造者模式解决盖房需求应用实例

  1. /**
  2. * @author houyu
  3. * @createTime 2019/11/18 20:00
  4. */
  5. public class Demo1 {
  6. /**
  7. * 产品: 房屋
  8. */
  9. public static class House {
  10. private String wall;
  11. private String houseTop;
  12. public String getWall() {
  13. return wall;
  14. }
  15. public House setWall(String wall) {
  16. this.wall = wall;
  17. return this;
  18. }
  19. public String getHouseTop() {
  20. return houseTop;
  21. }
  22. public House setHouseTop(String houseTop) {
  23. this.houseTop = houseTop;
  24. return this;
  25. }
  26. @Override
  27. public String toString() {
  28. final StringBuilder sb = new StringBuilder("House{");
  29. sb.append(", wall='").append(wall).append('\'');
  30. sb.append(", houseTop='").append(houseTop).append('\'');
  31. sb.append('}');
  32. return sb.toString();
  33. }
  34. }
  35. /**
  36. * 抽象建造者: 房屋抽象构建者
  37. *
  38. * interface / abstract class
  39. */
  40. public interface HouseBuilder {
  41. /**
  42. * 做墙
  43. */
  44. void makeWall();
  45. /**
  46. * 做房顶
  47. */
  48. void makeHourTop();
  49. /**
  50. * 制作完成房子
  51. */
  52. House buildHouse();
  53. }
  54. /**
  55. * 具体构建者: 我的房屋构建者
  56. */
  57. public static class MyHouseBuilder extends House implements HouseBuilder {
  58. House house = new House();
  59. @Override
  60. public void makeWall() {
  61. house.setWall("MyHouseBuilder.makeWall");
  62. }
  63. @Override
  64. public void makeHourTop() {
  65. house.setHouseTop("MyHouseBuilder.makeHourTop");
  66. }
  67. @Override
  68. public House buildHouse() {
  69. /**
  70. * 由 具体构建者 进行构建
  71. */
  72. makeWall();
  73. makeHourTop();
  74. return house;
  75. }
  76. }
  77. /**
  78. * 指挥者: 我自己
  79. */
  80. public static class My {
  81. private HouseBuilder builder;
  82. public My setBuilder(HouseBuilder builder) {
  83. this.builder = builder;
  84. return this;
  85. }
  86. public House build() {
  87. return builder.buildHouse();
  88. }
  89. }
  90. public static void main(String[] args) {
  91. // 创建具体构建者
  92. HouseBuilder builder = new MyHouseBuilder();
  93. // 创建指挥者
  94. My my = new My();
  95. // 给指挥者传递构建者
  96. my.setBuilder(builder);
  97. // 指挥者调用构建者的具体流程构建完成返回房子
  98. House build = my.build();
  99. // 输出房子
  100. System.out.println("build.toString() = " + build.toString());
  101. /**
  102. * 这个案例的构建者模式是比较标准的, 很明确的存在4个角色
  103. *
  104. * 在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
  105. * 也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的...
  106. */
  107. }
  108. }

说明:
在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的…

6.2 构造者模式在 JDK 框架中源码分析(StringBuilder)

  1. // 产品
  2. public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
  3. }
  4. // 抽象构建者
  5. public interface Appendable {
  6. // ...
  7. Appendable append(CharSequence csq) throws IOException;
  8. }
  9. // 具体建造者 (AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能 实例化)
  10. abstract class AbstractStringBuilder implements Appendable, CharSequence {
  11. // ...
  12. public AbstractStringBuilder append(Object obj) {
  13. return append(String.valueOf(obj));
  14. }
  15. }
  16. // StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而 StringBuilder 继承了 AbstractStringBuilder
  17. public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
  18. // ...
  19. public StringBuilder(String str) {
  20. super(str.length() + 16);
  21. append(str);
  22. }
  23. }

6.3 构造者模式在 lombok 中的应用

6.3.1编写实体类

  1. import lombok.Builder;
  2. @Builder
  3. public class Friend {
  4. private String name;
  5. private Integer age;
  6. }

6.3.2编写使用类

  1. public class Test {
  2. public static void main(String[] args) throws Exception {
  3. // 下面一行代码充当了 指挥者 和 具体建造者
  4. Friend friend = Friend.builder().age(10).name("张三").build();
  5. System.out.println("friend = " + friend);
  6. }
  7. }

6.3.3反编译 Friend 的构建者

  1. public class Friend {
  2. private String name;
  3. private Integer age;
  4. Friend(String name, Integer age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public static Friend.FriendBuilder builder() {
  9. return new Friend.FriendBuilder();
  10. }
  11. // 这里没有抽象构建者, 只有具体构建者, 因此构建者模式不一定说一定要存在4个角色的
  12. public static class FriendBuilder {
  13. private String name;
  14. private Integer age;
  15. FriendBuilder() {
  16. }
  17. public Friend.FriendBuilder name(String name) {
  18. this.name = name;
  19. return this;
  20. }
  21. public Friend.FriendBuilder age(Integer age) {
  22. this.age = age;
  23. return this;
  24. }
  25. public Friend build() {
  26. return new Friend(this.name, this.age);
  27. }
  28. public String toString() {
  29. return "Friend.FriendBuilder(name=" + this.name + ", age=" + this.age + ")";
  30. }
  31. }
  32. }

6.4 建造者模式的注意事项和细节

1) 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可 以创建不同的产品对象
2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具 体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
3) 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰, 也更方便使用程序来控制创建过程
4) 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭 原则”
5) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使 用建造者模式,因此其使用范围受到一定的限制。
6) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因 此在这种情况下,要考虑是否选择建造者模式.
7) 抽象工厂模式 VS 建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采 用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定 的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品

  标签   设计模式、  
mail csdn

作者  :  houyu [后宇]

程序员[ 后宇 ],是一个关注编程,热爱技术的开发者,热衷于 【Java后端】,【数据爬虫】,【大数据】领域。

在这里会一直记录着我成长的点点滴滴,毕竟好记性不如烂笔头,如果你在博客中有所收获,这也将是我毕生的荣幸。如有差池,还望指出更正!



评论


暂时没有评论哦~,你来评论个吧!!