`
clearity
  • 浏览: 35507 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用Builders代替构造器创建不可变对象

 
阅读更多

本篇将拿构造器来和使用builder设计模式比较着解释应该如何创建不可变对象

使用此种方式可以使你的代码更具有可读性,首先,让我们来看下如果使用如下的接收三个BigDecimal类型参数的构造器来构建的CashBalance对象是多么的不优雅。然后我们会在看下使用builder模式是怎样让你的代码更简洁的。

 

当下次你在面试中被问及设计模式的时候,你可以拿这个实现来和使用很频繁的工厂模式和单例模式做个比较。这篇文章还设计到其他两个重要的概念(不可变性和线程安全)

 

 

import java.math.BigDecimal;
 
/**
 * 因为不可变,所以是线程安全的对象
 */
public final class CashBalance {
  
 private BigDecimal initialBalance;
 private BigDecimal totCredits;
 private BigDecimal totDebits;
  
 //构造器
 public CashBalance(BigDecimal initialBalance, BigDecimal totCredits, BigDecimal totDebits) {
  this.initialBalance = initialBalance;
  this.totCredits = totCredits;
  this.totDebits = totDebits;
 }
  
 //不可变对象中只提供getter方法,不提供setter方法
 
}

 

 

因此,这段代码哪里不优雅?首先它的构造器需要三个BigDecimal的参数,在使用类的时候对于参数的意义来说不怎么直观,你说不清哪个参数是initialBalance,哪个参数是totCredits,如果你使用下面的方式来调用构造器会更好

 

 

CashBalance bal = new CashBalance(initialBalance:BigDecimal.valueOf(250.00), 
                                  totCredits:BigDecimal.valueOf(250.00), 
          totDebits:BigDecimal.valueOf(250.00));

 

 

很不幸的是,你不能使用上面的语法来写程序,你只能通过下面的方式调用它.

 

 

CashBalance bal = new CashBalance(BigDecimal.valueOf(250.00), 
                                  BigDecimal.valueOf(250.00), 
          BigDecimal.valueOf(250.00));

 

 

通过一个空构造器和三个setter方法来实现代码会更为优雅,但是对象必须是不可变的。下面是拯救你的构造器设计模式的代码。通过定义一个内部类来构造CashBalance对象。

 

 

import java.math.BigDecimal;
 
/**
 * 不可变,所以线程安全
 */
public final class CashBalance {
  
 private BigDecimal initialBalance, totCredits, totDebits;
  
 //构造器
 public CashBalance(CashBalanceBuilder builder) {
  this.initialBalance = builder.initialBalance;
  this.totCredits = builder.totCredits;
  this.totDebits = builder.totDebits;
 }
  
 //构造器模式
 public static class CashBalanceBuilder {
   
  // 有需要构造的对象一致的字段列表
  protected BigDecimal initialBalance, totCredits, totDebits;
 
  //将访问权限设置为包内访问
  void setInitialBalance(BigDecimal initialBalance) {
   this.initialBalance = initialBalance;
  }
 
  void setTotCredits(BigDecimal totCredits) {
   this.totCredits = totCredits;
  }
 
  void setTotDebits(BigDecimal totDebits) {
   this.totDebits = totDebits;
  } 
 }
  
 //只提供setter方法
 
}

 

 

现在,你可以像下面调用一样在类外面构造CashBalance

 

 

public static void main(String[] args) {
 CashBalance.CashBalanceBuilder builder = new CashBalance.CashBalanceBuilder();
 builder.setInitialBalance(BigDecimal.valueOf(250.00));
 builder.setTotCredits(BigDecimal.valueOf(250.00));
 builder.setTotDebits(BigDecimal.valueOf(250.00));
 CashBalance bal = new CashBalance(builder);
}

 

 

上面的代码完成了,但是如果你还有更多的字段,构造的代码就会显得很臃肿。这个可以用下面的方式来改善。

 

下面代码的改进是通过修改void类型的setter方法设值后返回builder自身来实现的

 

import java.math.BigDecimal;
 
/**
 * 不可变线程安全对象
 */
public final class CashBalance {
  
 private BigDecimal initialBalance, totCredits, totDebits;
  
 //构造器
 public CashBalance(CashBalanceBuilder builder) {
  this.initialBalance = builder.initialBalance;
  this.totCredits = builder.totCredits;
  this.totDebits = builder.totDebits;
 }
  
  
 public static class CashBalanceBuilder {
   
  //has same fields as the object it is going to build
  protected BigDecimal initialBalance, totCredits, totDebits;
 
  //define the setters that return itself
  CashBalanceBuilder setInitialBalance(BigDecimal initialBalance) {
   this.initialBalance = initialBalance;
   return this;
  }
 
  CashBalanceBuilder setTotCredits(BigDecimal totCredits) {
   this.totCredits = totCredits;
   return this;
  }
 
  CashBalanceBuilder setTotDebits(BigDecimal totDebits) {
   this.totDebits = totDebits;
   return this;
  } 
 }
  
 
}

 

代码修改带来的优雅是现在可以通过下面的构造方式调用:

 

public static void main(String[] args) {
   CashBalance.CashBalanceBuilder builder = new CashBalance.CashBalanceBuilder()
     .setInitialBalance(BigDecimal.valueOf(250.00))
     .setTotCredits(BigDecimal.valueOf(250.00))
     .setTotDebits(BigDecimal.valueOf(250.00));
   CashBalance bal = new CashBalance(builder);
}

 

因此这些秘诀可以让你在和职场达人的面试中赢得加分

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics