当构造方法的参数过多时怎么使用builder模式
这篇文章将为大家详细讲解有关当构造方法的参数过多时怎么使用builder模式,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
一、传统方式的缺点
1、可伸缩构造方法
可伸缩构造方法就是我们平时书写最常见的那种,请看下文代码;
public class Student { private int id; //必要 private String name;//必要 private int age; //可选 private int sclass; //可选 private int height;//可选 private float weight;//可选 private float score;//可选 //构造函数1:默认构造方法 public Student() {}; //构造方法2:必要字段构造方法 public Student(int id, String name) { this.id = id; this.name = name; } //构造方法3:全部字段构造方法 public Student(int id, String name, int age, int sclass, int height, float weight, float score) { super(); this.id = id; this.name = name; this.age = age; this.sclass = sclass; this.height = height; this.weight = weight; this.score = score; } }
下面如果我们要创建一个Student类,一般这样创建,看下面代码:
public class Main { public static void main(String[] args) { //1、可伸缩构造方法 Student student1 = new Student(); Student student2 = new Student(1,"愚公要移山"); Student student3 = new Student(2,"愚公要移山",18,1,175,120,99); } }
现在我们列举了一个具有七个字段的例子,比较容易理解,现在我们来分析一下,他有什么缺点:
缺点1:反转字段,编译器不会报错
比如上面的字段里面有一个weight和一个score,都是float类型,如果再new一个Student类时,不小心写反了,编译器不会察觉。
缺点2:难以理解
这里只是七个字段,如果有十几个,我们就需要不断地去Student类中去查看,看看第几个参数应该写哪些东西,实在是比较麻烦。用户在看到这个Student(2,"愚公要移山",18,1,175,120,99)无法理解每一个字段属性代表的是什么含义。
缺点3:不想设置的参数,却不得不设置值
有时候我们的Student只想着设置ID、name和age字段,其他的无关紧要,但是这种模式必须要设置所有的属性值。
既然上面有这些缺点,我们可能还想到另外一种方式,那就是javaBean。
2、javaBean模式
先看javaBean模式如何写的。
public class Student { private int id; //必要 private String name;//必要 private int age; //可选 private int sclass; //可选 private int height;//可选 private float weight;//可选 private float score;//可选 //构造函数1:默认构造方法 public Student() {} //getter和setter方法 public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} public int getSclass() {return sclass;} public void setSclass(int sclass) {this.sclass = sclass;} public int getHeight() {return height;} public void setHeight(int height) {this.height = height;} public float getWeight() {return weight;} public void setWeight(float weight) {this.weight = weight;} public float getScore() {return score;} public void setScore(float score) {this.score = score;}; }
这种模式,看起来还比较舒服,只是设置了相应的getter和setter方法。再来看看如何使用这种方式去new一个Student类。
public class Main { public static void main(String[] args) { //2、javaBean模式 Student student1 = new Student(); student1.setId(1); student1.setName("愚公要移山"); student1.setSclass(1); student1.setWeight(180); student1.setHeight(175); student1.setScore(100); student1.setAge(20); } }
这样看起来还可以,不过这只是我自己一个一个敲出来的。实际在用的时候就知道同样恶心了,现在来总结一波他的缺点。
缺点1:构造过程中 JavaBean可能处于不一致的状态
JavaBeans 模式本身有严重的缺陷。由于构造方法在多次调用中被分割,所以在构造过程中 JavaBean 可能处于不一致的状态。该类没有通过检查构造参数参数的有效性来执行一致性的选项。在不一致的状态下尝试使用对象可能会导致与包含 bug 的代码大相径庭的错误,因此很难调试。
说一下我对其的理解,在上面的例子中,我们的student1对象被多次调用了set方法,但是可能有时候在用到这个bean时,剩下的setter方法还没有做完,于是再次调用时发现同一个javaBean呈现出了两种状态。于是处于一种不一致的状态。
缺点2:无法保证javaBean的不可变性
使用第一种模式可伸缩构造方法实例化之后不会更改可变性,所有的数据都是确定好了的。也可以保证线程安全。但是提供了setter方法,就不能保证了。比如:
public class Main { public static void main(String[] args) { //2、javaBean模式 Student student1 = new Student(); student1.setId(1); student1.setName("愚公要移山"); student1.setSclass(1); student1.setWeight(180); student1.setHeight(175); student1.setScore(100); student1.setAge(20); System.out.println(student1.getName()); student1.setName("冯冬冬"); System.out.println(student1.getName()); } } //输出结果:愚公要移山 冯冬冬
可以看到,我们可以对Student对象设置多次name,前后是不一致的状态。
既然前面两种都存在各种各样的问题。现在我们再来看今天的主题builder模式,
二、builder模式
还是老样子,我们先看看builder模式长得什么样子。再来分析一下他的优缺点。
public class Student { private int id; // 必要 private String name;// 必要 private int age; // 可选 private int sclass; // 可选 private int height;// 可选 private float weight;// 可选 private float score;// 可选 public Student(Builder builder) { this.id = builder.id; this.name = builder.name; this.age = builder.age; this.sclass = builder.sclass; this.height = builder.height; this.weight = builder.weight; this.score = builder.score; } public static class Builder { private int id; // 必要 private String name;// 必要 private int age; // 可选 private int sclass; // 可选 private int height;// 可选 private float weight;// 可选 private float score;// 可选 // 必要参数的构造方法 public Builder(int id, String name) { this.id = id; this.name = name; } public Builder setId(int id) { this.id = id; return this; } public Builder setName(String name) { this.name = name; return this; } public Builder setAge(int age) { this.age = age; return this; } public Builder setSclass(int sclass) { this.sclass = sclass; return this; } public Builder setHeight(int height) { this.height = height; return this; } public Builder setWeight(float weight) { this.weight = weight; return this; } public Builder setScore(float score) { this.score = score; return this; } // 对外提供的 public Student build() { return new Student(this); } } }
上面的代码是在内部构造了一个Builder类,然后我们看看如何去使用。
public class Main { public static void main(String[] args) { //3、Builder模式 Student stu = new Student.Builder(1, "愚公要移山") .setAge(20) .setHeight(175) .setSclass(1) .setScore(100) .setWeight(100).build(); } }
这本书中对其的缺点也进行了介绍,很直观可以看到,Student类中的代码量增加了很多。但是Student类,我们只需要写一次,这却为我们创建对象带来了方便。
优点1:不存在反转字段的情况
上面可以看出,每次添加新字段值的时候是通过set方式进行的。具有javaBean的优点。
优点2:灵活构造参数
我们把必要的字段一写,那些非必要的字段我们可以自己选择是不是要set。
优点3:不存在不一致状态
使用builder模式,对象的创建必须要等到build完成才可以。
优点4:使用灵活
单个 builder 可以重复使用来构建多个对象。builder 的参数可以在构建方法的调用之间进行调整,以改变创建的对象。builder 可以在创建对象时自动填充一些属性,例如每次创建对象时增加的序列号。
缺点:
为了创建对象,首先必须创建它的 builder。虽然创建这个 builder 的成本在实践中不太可能被注意到,但在性能关键的情况下可能会出现问题。而且,builder 模式比伸缩构造方法模式更冗长,因此只有在有足够的参数时才值得使用它,比如四个或更多。
但是,如果从构造方法或静态工厂开始,并切换到 builder,当类演化到参数数量失控的时候,过时的构造方法或静态工厂就会面临尴尬的处境。因此,所以,最好从一开始就创建一个 builder。
关于当构造方法的参数过多时怎么使用builder模式就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。