第2条:遇到多个构造器参数时要考虑用构建器

本文介绍了软件设计中的Builder模式,探讨了其在解决构造器参数过多问题上的应用,通过具体示例展示了如何利用Builder模式简化对象创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

术语:

重叠构造器(telescoping construct)模式:就是提供多个构造函数。
JavaBeans模式:这种模式实际上是先调用无参的构造函数来创建对象,然后再调用属性的setter方法来进行相应设置。
Builder模式:这种模式下不直接生成需要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂方法)。得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法来设置相关参数,最后调用build方法来生成不可变的对象,这个builder是它构建的类的静态成员类。
静态工厂方法和构造器方法都不能很好的支持大量可选参数的情况,如果有大量的参数要设置,这两种方法可能都需要提供很多个函数来实现需求。并且对于单一的方法函数的参数顺序一旦固定则,仅最后一个参数可作为变参数(如果需要的话)。调用函数的时候你不得不为那些你并不关心的属性提供相应的值,并且随着参数的不断增多,这种方式越来越让人难以忍受。
遇到了多参数的构造器的时候,有一种补救办法是使用JavaBeans模式,这种模式的问题在于构造的过程被分到了多个步骤上,这样一来可能会使JavaBean处于不一致的状态,这会导致失败,调试起来十分困难。还有一方面,这种模式阻止了把类做成不可变类的可能,这就需要用其他的方法来确保它是线程安全的。
当对象的构造完成,并且不允许在解冻之前使用时,通过手动的解冻对象可以弥补这些不足,但是这种方式很少使用,而且也不很容易出错。
另外一种方法是Builder模式,所示例子如下

// Builder Pattern  
public class NutritionFacts {  
    private final int servingSize;  
    private final int servings;  
    private final int calories;  
    private final int fat;  
    private final int sodium;  
    private final int carbohydrate;  

    public static class Builder {  
        // Required parameters  
        private final int servingSize;  
        private final int servings;  

        // Optional parameters - initialized to default values  
        private int calories = 0;  
        private int fat = 0;  
        private int carbohydrate = 0;  
        private int sodium = 0;  

        public Builder(int servingSize, int servings) {  
            this.servingSize = servingSize;  
            this.servings = servings;  
        }  

        public Builder calories(int val) {  
            calories = val;  
            return this;  
        }  
        public Builder fat(int val) {  
            fat = val;  
            return this;  
        }  
        public Builder carbohydrate(int val) {  
            carbohydrate = val;  
            return this;  
        }  
        public Builder sodium(int val) {  
            sodium = val;  
            return this;  
        }  

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

    private NutritionFacts(Builder builder) {  
        servingSize = builder.servingSize;  
        servings = builder.servings;  
        calories = builder.calories;  
        fat = builder.fat;  
        carbohydrate = builder.carbohydrate;  
        sodium = builder.sodium;  
    }  
}  

可见,buidler模式是利用了一个静态的内部类来生成一个中间对象,然后利用类似JavaBeans的模式来获取到所有所需的参数,再来调用目标类的构造函数。由于内部类的setter方法都返回的是自身的引用,所以可以写出如下风格的代码

NutritionFacts cocaCola = new NutritionFacts(240,8).  
    calories(100).sodium(3).carbohydrate(23).build();  

这样,客户端的代码可容易编写和阅读。 Builder模式可以对其参数加以约束,build方法可以检验这些约束条件,将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验,如果违反了任何约束,build方法就应该抛出IllegalStateException。另一种强加约束条件的方法是用多个setter方法对某个约束条件必须持有的所有参数进行检查。如果没有得到满足,则setter应该抛出IllegalArgumentException。这样一旦传递的参数无效就会发现约束条件失败,而不用推迟到调用build时。

设置了参数的builder生成了一个很好的抽象工厂,用这种方法,需要有个类型来表示builder,一般可以选择泛型来做一个满足所有条件的builder。

// A builder for objects of type T  
public interface Builder<T> {  
    public T build();  
}  

带有Builder实例的方法通常利用有限制的通配符类型来约束构建器的类型参数,例如,利用不同的结点的构建器来初始化树的方式如下

Tree buildTree(Builder<? extends Node> nodeBuilder) {}  

newInstance方法充当build的替代方案有很多问题,newInstance方法总是企图调用类的无参构造器,它甚至可能并不存在,客户端的代码必需要处理相应的异常,而且newInstance还会把调用无参构造器时所产生的异常再次抛出,即使newInstance方法缺乏相应的throws子句时也一样。这会破坏编译时的异常检查。


Builder模式的不足体现在为了创建对象必须先创建一个中间对象,builder模式还使得构造的过程更加冗长。如果在设计一个类的时候这个类就的构造器需要大量的参数或者这个类的构造器在不断改进迭代的过程中需要不断增长参数,那么在设计类的起初就应该使用构建器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值