使用Builder設計模式實現不變性 - DZone Java

banq發表於2019-01-27

Effective Java的一條建議是,除非有充分的理由讓它們變得可變,否則你應該讓你的類不可變。如果一個類不能成為不可變的,那麼儘可能地限制它的可變性。不可變類定義了一旦建立,就永遠不會改變其狀態的物件。所有狀態資訊都是在構造物件時提供的,並且在物件的生命週期內不會改變。

我們為什麼要編寫不可變類?
不可變類提供了許多優於可變類的優點。這些是:

  1. 不可變物件簡單易用,因為它只能處於一個狀態,即建立它的狀態。
  2. 它們本質上是執行緒安全的,即它們不需要同步。
  3. 不可變類的物件可以自由共享。例如,Boolean類重用其現有例項TRUE和FALSE,每當呼叫Boolean.valueOf方法時,它都會為您提供已建立的例項。


建立一個不可變類
一個簡單的不可變類可以是這樣的:


public final class User {
    private final String username;
    private final String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}


這個類是不可變的,因為:
  1. 它不提供setter方法或mutator。
  2. Class不能擴充套件,因為它是最終的。這也可以透過使建構函式私有來完成。
  3. class的欄位都是最終的和私人的。

值得注意的是,這個類非常簡單,只有兩個欄位。在我們的實際應用程式的大多數類中,有兩個以上的欄位。此外,大多數這些欄位對於物件建立不是必需的。例如,真實應用程式中的使用者將具有使用者名稱,密碼,名字,姓氏,creationDate,emailAddress等,但是對於此處的使用者建立,僅需要使用者名稱和密碼。所以,我們設計我們的類如下所示:


final class User {
    private final String username;
    private final String password;
    private String firstname;
    private String lastname;
    private String email;
    private Date creationDate;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
        creationDate = new Date();
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getCreationDate() {
        return new Date(creationDate.getTime());
    }
}


這個類不是不可變的,因為它有mutators,即setter。因此,可以在建立後修改此類的例項。這種方法的缺點是,物件可能處於不一致的狀態,透過它們的構造方式,你必須付出額外的努力來確保執行緒安全。

Builder Design Pattern來救援
據Gof說:
構建器模式將複雜物件的構造與其表示分開,以便相同的構造過程可以建立不同的表示。
構建器設計模式為您提供了構建複雜不可變物件的方法。過程是:

  1. 客戶端使用所有必需引數呼叫建構函式(或靜態工廠)並獲取構建器物件。
  2. 客戶端呼叫setter之類的方法來設定每個感興趣的可選引數。
  3. 最後,客戶端呼叫構建方法來生成不可變的物件。

不可變的使用者:


public class ImmutableUser {
    private final String username;
    private final String password;
    private final String firstname;
    private final String lastname;
    private final String email;
    private final Date creationDate;

    private ImmutableUser(UserBuilder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.creationDate = builder.creationDate;
        this.firstname = builder.firstname;
        this.lastname = builder.lastname;
        this.email = builder.email;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public String getEmail() {
        return email;
    }

    public Date getCreationDate() {
        return new Date(creationDate.getTime());
    }

    public static class UserBuilder {
        private final String username;
        private final String password;
        private final Date creationDate;
        private String firstname;
        private String lastname;
        private String email;

        public UserBuilder(String username, String password) {
            this.username = username;
            this.password = password;
            this.creationDate = new Date();
        }

        public UserBuilder firstName(String firsname) {
            this.firstname = firsname;
            return this;
        }

        public UserBuilder lastName(String lastname) {
            this.lastname = lastname;
            return this;
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public ImmutableUser build() {
            return new ImmutableUser(this);
        }
    }
}


您還應檢查構建方法中的不變數,如果任何屬性無效,則丟擲IllegalStateException。這將確保物件在例項化後處於可工作狀態。

public static void main(String[] args) {
      ImmutableUser user = new ImmutableUser.UserBuilder("shekhar", "password").firstName
                ("shekhar").lastName("gulati").email("shekhargulati84@gmail.com").build();
}


透過這種方式,您可以構建一個不可變的複雜物件,並具有不可變物件的所有優點。​​​​​​​

 

相關文章