使用Builder设计模式实现不变性 - DZone Java

19-01-27 banq
              

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();
}

通过这种方式,您可以构建一个不可变的复杂对象,并具有不可变对象的所有优点。​​​​​​​