您的位置:首页 > 产品设计 > UI/UE

多参构造器与Builder模式

2013-10-28 16:50 155 查看
一、静态工厂方法与构造器共同的局限性

不能很好的扩展到大量的可选参数,这个很悲剧的,我一般使用java bean,不管合适不合适,没考虑线程安全哈。

二、有多个可选参数对象的实例化方法

1、重叠构造器:提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个构造器有两个可选参数,以此类推,最后一个构造器包含所有的可选参数。

/**
 * 食品营养成分标签类 
 */
public class NutritionFacts {
	private final int servingSize; //(ml)          required
	private final int servings; //(per container)  required
	private final int calories; //                 optional
	private final int fat;      //(g)              optional
	private final int sodium;   //(mg)             optional
	private final int carbohydrate; //(g)          optional
	
	/**
	 * 只含必需项 
	 */
	public NutritionFacts(int servingSize,int servings){
		this(servingSize,servings,0);
	}
	/**
	 * 含有一个可选项 
	 */
	public NutritionFacts(int servingSize,int servings,int calories){
		this(servingSize,servings,calories,0);
	}
	/**
	 * 含有两个可选项
	 */
	public NutritionFacts(int servingSize,int servings,int calories,int fat){
		this(servingSize,servings,calories,fat,0);
	}
	/**
	 * 含有三个可选项 
	 */
	public NutritionFacts(int servingSize,int servings,int calories,int fat,int sodium){
		this(servingSize,servings,calories,fat,sodium,0);
	}
	/**
	 * 含有所有可选项(4个)
	 */
	public NutritionFacts(int servingSize,int servings,int calories,int fat,int sodium,
			int carbohydrate){
		this.servingSize = servingSize;
		this.servings = servings;
		this.calories = calories;
		this.fat = fat;
		this.sodium = sodium;
		this.carbohydrate = calories;
	}
}

当想要创建实例的时候,只需使用参数列表最短的构造器,但有时需要设置很多本来不需要设置的参数,参数少了还可以,如果多的话就成灾难了。

//这样构造方法需要设置一些本不需要设置的参数
		NutritionFacts nf = new NutritionFacts(120, 230, 456, 0, 30, 40);


点评:重叠构造器模式可行,但是当有许多参数的时候,客户代会很难编写,并且仍然较难以阅读。参数顺序及代表意义很难分辨,这需要良好的文档支持,但一般看文档比较费事。

2、JavaBean模式:调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。

public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize  = -1;  // Required; no default value
    private int servings     = -1;  //     "     "     "      "
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }

    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts();
        cocaCola.setServingSize(240);
        cocaCola.setServings(8);
        cocaCola.setCalories(100);
        cocaCola.setSodium(35);
        cocaCola.setCarbohydrate(27);
    }
}

优点:弥补了重叠构造器的不足,创建实例很容易,代码更容易阅读,我一般就是采用这种方式,哈哈。

缺点:

1、对象构造过程被分配到几个调用中,在构造过程中JavaBean可能处于不一致的状态,这样在多线程环境下容易产生线程安全问题。

2、JavaBean模式阻止了把类做成不可变类的可能,这需要我们自己付出额外努力来确保对象的线程安全。不可变类就是爽啊,不用考虑线程安全问题。无状态类也不用考虑,呵呵。貌似可以通过在构造完成之前,先冻结对象,构造完成之后再使用,这操作起来比较麻烦,很少这样做!

3、Builder模式:不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器或静态工厂,得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。通常,这个builder是它构造的类的静态成员类。

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;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
            calories(100).sodium(35).carbohydrate(27).build();
    }
}

优点:

1、即能保证像重叠构造器模式那样的安全性,又能保证像JavaBean模式那么好的可读性。

2、builder模式模拟了具名的可选参数,就像Ada和Python一样。没用过这两种语言,不知道怎么回事,呵呵。

3、builder模式可以对其参数强加约束条件。build方法可以检验这些约束条件,将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验。如果违反了任何约束条件,build方法都应该抛出IllegalStateException。异常的信息应该显示出违反了哪个约束条件。

缺点:

1、为了创建对象,必须先创建它的构造器。注重性能的情况下,可能就完蛋了。

2、Builder模式比重叠构造器更加冗长,一般在很多参数时才会使用,比如4个或更多。

三、最佳实践

1、如果类的构造器或静态工厂中有多个参数,设计这样类时,最好使用Builder模式,特别是当大多数参数都是可选的时候。

2、如果现在不能确定参数的个数,最好一开始就使用构建器即Builder模式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: