背景

当一个类的内部数据过于复杂的时候(通常是负责持有数据的类,比如Config、VO、PO、Entity…),要创建的话可能就需要了解这个类的内部结构,还有这些东西是怎么组织装配等一大坨乱七八糟的东西,这个时候就会增加学习成本而且会很混乱,这个时候就想啊想一种什么法子来管理一下这个类中的数据呢,怎么在创建的时候让它按部就班的来,并且代码可读性很好别让我看花了眼啊,我要的东西也能都很好设置进来,这就是Builder模式的应用场景,Builder模式可以将一个类的构建和表示进行分离。

1.介绍

1.1 什么是构建者模式

创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使
得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。

1.2 适用场景:

  • 隔离复杂对象的创建和使用,相同的方法,不同执行顺序,产生不同事件结果
  • 多个部件都可以装配到一个对象中,但产生的运行结果不相同
  • 产品类非常复杂或者产品类因为调用顺序不同而产生不同作用
  • 初始化一个对象时,参数过多,或者很多参数具有默认值

1.3 主要作用

在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

  • 用户只需要给出指定复杂对象的类型和内容;
  • 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

    2. 模式原理

    2.1 UML类图 & 组成

    image.png

3.用 builder 模式创建共享单车为例子,示例代码:

  1. public class Bike {
  2. private IFrame frame;
  3. private ISeat seat;
  4. private ITire tire;
  5. }
  1. // 抽象 builder 类
  2. public abstract class Builder {
  3. // 声明具体建造者的公共接口(产品的构建过程)
  4. abstract void buildFrame();
  5. abstract void buildSeat();
  6. abstract void buildTire();
  7. // 返回产品的方法
  8. abstract Bike createBike();
  9. }
  1. // 具体 builder 类
  2. public class MobikeBuilder extends Builder{
  3. private Bike mBike = new Bike();
  4. @Override
  5. void buildFrame() {
  6. mBike.setFrame(new AlloyFrame());
  7. }
  8. @Override
  9. void buildSeat() {
  10. mBike.setSeat(new DermisSeat());
  11. }
  12. @Override
  13. void buildTire() {
  14. mBike.setTire(new SolidTire());
  15. }
  16. @Override
  17. Bike createBike() {
  18. return mBike;
  19. }
  20. }
  21. // ofo单车构建者
  22. public class OfoBuilder extends Builder{
  23. private Bike mBike = new Bike();
  24. @Override
  25. void buildFrame() {
  26. mBike.setFrame(new CarbonFrame());
  27. }
  28. @Override
  29. void buildSeat() {
  30. mBike.setSeat(new RubberSeat());
  31. }
  32. @Override
  33. void buildTire() {
  34. mBike.setTire(new InflateTire());
  35. }
  36. @Override
  37. Bike createBike() {
  38. return mBike;
  39. }
  40. }
  1. // 负责产品对象的生产过程
  2. // 控制调用先后次序,并向调用者返回完整的产品类
  3. public class Director {
  4. private Builder mBuilder = null;
  5. public Director(Builder builder) {
  6. mBuilder = builder;
  7. }
  8. public Bike construct() {
  9. mBuilder.buildFrame();
  10. mBuilder.buildSeat();
  11. mBuilder.buildTire();
  12. return mBuilder.createBike();
  13. }
  14. }

客户端使用:

  1. public class Click {
  2. public static void main(String[] args) {
  3. showBike(new OfoBuilder());
  4. showBike(new MobikeBuilder());
  5. }
  6. private void showBike(Builder builder) {
  7. Director director = new Director(builder);
  8. Bike bike = director.construct();
  9. bike.getFrame().frame();
  10. bike.getSeat().seat();
  11. bike.getTire().tire();
  12. }
  13. }

上面示例是 Builder模式的常规用法,导演类 Director 在 Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合,示例代码:

  1. public abstract class NewBuilder {
  2. abstract void buildFrame();
  3. abstract void buildSeat();
  4. abstract void buildTire();
  5. abstract Bike createBike();
  6. /**
  7. * 把导演类中的construct()方法合并到抽象建造者类中
  8. *
  9. * @return 具体产品对象
  10. */
  11. public Bike construct() {
  12. this.buildFrame();
  13. this.buildSeat();
  14. this.buildTire();
  15. return this.createBike();
  16. }
  17. }

这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果construct() 过于复杂,建议还是封装到 Director 中

4.用 builder 模式创建对象,参数很多:

除了上面的用途外,还有另外一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用 builder模式进行重构,重构前示例代码:

  1. // 省略 getter 和 setter 方法
  2. public class Computer {
  3. private String cpu;
  4. private String screen;
  5. private String memory;
  6. private String mainboard;
  7. public Computer(String cpu, String screen, String memory, String mainboard) {
  8. this.cpu = cpu;
  9. this.screen = screen;
  10. this.memory = memory;
  11. this.mainboard = mainboard;
  12. }
  13. }
  14. // builder设计模式
  15. public class NewComputer {
  16. private String cpu;
  17. private String screen;
  18. private String memory;
  19. private String mainboard;
  20. public NewComputer() {
  21. throw new RuntimeException(“cant init”);
  22. }
  23. private NewComputer(Builder builder) {
  24. cpu = builder.cpu;
  25. screen = builder.screen;
  26. memory = builder.memory;
  27. mainboard = builder.mainboard;
  28. }
  29. public static final class Builder {
  30. private String cpu;
  31. private String screen;
  32. private String memory;
  33. private String mainboard;
  34. public Builder() {}
  35. public Builder cpu(String val) {
  36. cpu = val;
  37. return this;
  38. }
  39. public Builder screen(String val) {
  40. screen = val;
  41. return this;
  42. }
  43. public Builder memory(String val) {
  44. memory = val;
  45. return this;
  46. }
  47. public Builder mainboard(String val) {
  48. mainboard = val;
  49. return this;
  50. }
  51. public NewComputer build() {
  52. return new NewComputer(this);}
  53. }
  54. }

客户端:

  1. public class Click {
  2. public static void main(String[] args) {
  3. // 非 Builder 模式
  4. Computer computer = new Computer(“cpu”, screen”, memory”, mainboard”);
  5. // Builder 模式
  6. NewComputer newComputer = new NewComputer.Builder()
  7. .cpu(“cpu”)
  8. .screen(“screen”)
  9. .memory(“memory”)
  10. .mainboard(“mainboard”)
  11. .build();
  12. }
  13. }

在Java世界里,经常被提到静态这个概念,static作为静态成员变量和成员函数的修饰符,意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。最近一个项目里频繁用到static修饰的内部类,再读了一下《Effective Java》才明白为什么会用static来修饰一个内部类也就是本文的中心——静态类。

如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。在一番调查后个人总结出了3点关于内部类和静态内部类(俗称:内嵌类)

  • 静态内部类跟静态方法一样,只能访问静态的成员变量和方法,不能访问非静态的方法和属性,但是普通内部类可以访问任意外部类的成员变量和方法
  • 静态内部类可以声明普通成员变量和方法,而普通内部类不能声明static成员变量和方法。
  • 静态内部类可以单独初始化
    1. Inner i = new Outer.Inner();
    普通内部类初始化:
    1. Outer o = new Outer();
    2. Inner i = o.new Inner();

    参考

    https://www.jianshu.com/p/3d1c9ffb0a28