概要 設計模式類型:創建型 目標問題:創建對象時,參數設置的靈活性問題。(具體看案例) 接下來我們看一個需要改進的案例。 對象創建的優化 現在有個Employee類,你能預想到在開發中可能會出現的問題嗎?不一定是業務方面的問題哦。 最初版 public class Employee { privat ...
概要
- 設計模式類型:創建型
- 目標問題:創建對象時,參數設置的靈活性問題。(具體看案例)
接下來我們看一個需要改進的案例。
對象創建的優化
現在有個Employee
類,你能預想到在開發中可能會出現的問題嗎?不一定是業務方面的問題哦。
最初版
public class Employee {
private String name;
private String sex;
private int age;
private String address; // 住址
private String post; // 郵編
private String company; // 公司
private String department; // 部門
public Employee(String name, String sex, int age,
String address, String post, String company,
String department) {
this.name = name;
this.sex = sex;
if (!("男".equals(sex) || "女".equals(sex))) {
throw new RuntimeException("輸入錯誤的性別:" + sex);
}
this.age = age;
if (age <= 1 || age >= 150) {
throw new RuntimeException("輸入錯誤的年齡:" + age);
}
this.address = address;
this.post = post;
if (!postCheck()) {
throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
}
this.company = company;
this.department = department;
}
private boolean postCheck() {
if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
return false;
}
return true;
}
}
實際上在業務上並沒有過多的問題,最重要的問題就是這個類的使用非常的麻煩。
首先,構造函數只有一個,如果不增加新的構造函數的話無法靈活的傳入不同數量的參數。並且為了讓其參數的設置變得靈活,我們必須重載非常多種不一樣的構造函數,工程量巨大,且枯燥乏味。
為此,我們有了一個新的改進方案。
修改版v1
public class Employee {
private String name;
private String sex;
private int age;
private String address; // 住址
private String post; // 郵編
private String company; // 公司
private String department; // 部門
public Employee() {}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
if (!("男".equals(sex) || "女".equals(sex))) {
throw new RuntimeException("輸入錯誤的性別:" + sex);
}
}
public void setAge(int age) {
this.age = age;
if (age <= 1 || age >= 150) {
throw new RuntimeException("輸入錯誤的年齡:" + age);
}
}
public void setAddress(String address) {
this.address = address;
}
public void setPost(String post) {
this.post = post;
if (!postCheck()) {
throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
}
}
public void setCompany(String company) {
this.company = company;
}
public void setDepartment(String department) {
this.department = department;
}
private boolean postCheck() {
if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
return false;
}
return true;
}
}
改成了上述方式後確實使用起來方便不少,不想傳入的參數不調用對應的set
方法就好了,並且也省去了大量構造方法的定義。但是,依舊有一個問題,我們來看看使用樣例。
public static void main(String[] args) {
Employee e = new Employee();
e.setPost("121-1245-1231"); // 地址(null)與郵編(121-1245-1231)不一致,報錯
}
由於設置post
的時候對於address
屬性做了非空判斷,所以代碼書寫時address
的設置一定要在post
之前,否則就會報錯。這無疑增加了項目開發的難度。
既然構造函數麻煩,set
也存在一些問題。那我們如何優化Employee
對象的創建呢?看下麵的樣例。
修改版v2
public class Employee {
private String name;
private String sex;
private int age;
private String address; // 住址
private String post; // 郵編
private String company; // 公司
private String department; // 部門
private Employee(Builder builder) {
this.name = builder.name;
this.sex = builder.sex;
this.age = builder.age;
this.address = builder.address;
this.post = builder.post;
this.company = builder.company;
this.department = builder.department;
};
public static Builder Builder() {
return new Builder();
}
public static class Builder {
private String name;
private String sex;
private int age;
private String address; // 住址
private String post; // 郵編
private String company; // 公司
private String department; // 部門
public Builder name(String name) {
this.name = name;
return this;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Builder post(String post) {
this.post = post;
return this;
}
public Builder company(String company) {
this.company = company;
return this;
}
public Builder department(String department) {
this.department = department;
return this;
}
public Employee build() {
if (!("男".equals(sex) || "女".equals(sex))) {
throw new RuntimeException("輸入錯誤的性別:" + sex);
}
if (age <= 1 || age >= 150) {
throw new RuntimeException("輸入錯誤的年齡:" + age);
}
if (!postCheck()) {
throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
}
return new Employee(this);
}
private boolean postCheck() {
if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
return false;
}
return true;
}
}
}
使用建造者模式優化對象的創建。客戶端中對象的創建不再使用new
關鍵字,不需要定義數量繁多的構造函數以應對複雜多變的屬性設置,並且也有著set
的靈活性又不存在屬性設置順序的依賴。以下是使用樣例。
public static void main(String[] args) {
Employee e = Employee.Builder().name("張三").sex("男").age(35)
.post("124-1241-1352").address("江西省南昌市").build(); // 執行成功
}
流式編程的風格使得代碼清晰舒爽。
總結
優點
- 無需定義大量的構造方法。
- 既有
set
方法的靈活性,又消除了set
可能出現的屬性固定順序設置的問題。 - 流式編程的風格,代碼清晰簡潔。
缺點
- 由於定義了靜態內部類
Builder
,可能會使得系統的類數量激增,影響性能。 - 代碼的理解難度增加。
適用場景
- 適用於屬性很多的類的創建。
- 適用於對屬性設置判斷條件複雜的類的創建。尤其是屬性設置對於其他屬性有依賴的情況。
本文來自博客園,作者:buzuweiqi,轉載請註明原文鏈接:https://www.cnblogs.com/buzuweiqi/p/16709149.html