zoukankan      html  css  js  c++  java
  • 遇到多个构造器参数时要考虑用构建器

    静态工厂和构造器都有一个共同的局限性:它们都不能很好地扩展到大量的可选参数。考虑用一个类表示食品外包装的营养成分标签。这些标签中的有几个域是必须的:每份的分量,每罐的含量以及每份的卡路里,还有超过20个的可选域:总脂肪量,饱和脂肪量,转化脂肪,胆固醇等等,大多数产品在某几个域中都会有非零的值。

    对于这样的类,应该用哪种构造器或者静态方法来编写呢?程序员一向习惯用重叠构造器(telescoping constructor)模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有可选参数。下面有个示例,为简便起见,只显示四个可选域:

    Public class NutritionFacts {
    	private final int servingSize;//ml required
    	private final int serings;// per container required
    	private final int calories;//optional
    	private final int fat;//g optional
    	private final int sodium;//mg optional
    	private final int carbonhydrate;//g optional
    	public NutritionFacts(int servingSize,int serings){
    		this(servingSize,servings,0);
    	}
    	
    	public NutritionFacts(int servingSize,int serings,int calories){
    		this(servingSize,serings,calories,0);
    	}
    	
    	public NutritionFacts(int servingSize,int serings,int calories,fat){
    		this(servingSize,serings,calories,fat,0);
    	}
    	
    	public NutritionFacts(int servingSize,int serings,int calories,fat,sodium){
    		this(servingSize,serings,calories,fat,sodium,0);
    	}
    	
    	public NutritionFacts(int servingSize,int serings,int calories,fat,sodium,carbonhydrate){
    		this.servingSize = servingSize;
    		this.serings = servings;
    		this.calories = calories;
    		this.fat = fat;
    		this.sodium = sodium;
    		this.carbonhydrate = carbonhydrate;
    	}
    }
     

    当你要创建该类的实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:

    NutritionFacts cocacola = new NutritionFacts(240,8,100,0,35,27);

    这个构造器调用通常需要许多你不想设置的参数,但是还是需要为他们传值,如果随着参数数目的不断增加,那么这个构造器很快就失去控制了。在确定构造器参数少并且基本不会改变的的情况下,使用重叠构造器模式是可行的。在遇到许多构造器参数的时候,有一种替代方式:JavaBeans模式,在这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。

    public class NutritionFacts {
    	private int servingSize = -1 ;
    	private int serings = -1;
    	private int calories = 0;
    	private int fat = 0;
    	private int sodium = 0;
    	private int carbonhydrate = 0;
    	public void setServingSize(int servingSize) {
    		this.servingSize = servingSize;
    	}
    	public void setSerings(int serings) {
    		this.serings = serings;
    	}
    	public void setCalories(int calories) {
    		this.calories = calories;
    	}
    	public void setFat(int fat) {
    		this.fat = fat;
    	}
    	public void setSodium(int sodium) {
    		this.sodium = sodium;
    	}
    	public void setCarbonhydrate(int carbonhydrate) {
    		this.carbonhydrate = carbonhydrate;
    	}
    	public NutritionFacts(){}
    }

    这种模式弥补了重叠构造器模式的不足,就是创建实例很容易,产生的代码也很容易读。

    NutritionFacts cocacola = new NutritionFacts();
    		cocacola.setServingSize(240);
    		cocacola.setSerings(8);
    		cocacola.setCalories(100);
    		cocacola.setSodium(35);
    		cocacola.setCarbonhydrate(27);

    遗憾的是,JavaBeans你哦是自身有严重的缺点,因为构造过程被分到了几个调用中,在构造过车中JavaBeans可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。Javabeans模式组织了把类做成不可变的可能,需要程序员付出额外的努力来保证他的线程安全。

    幸运的是,我们有Builder模式作为替换。既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那样的易读性。不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个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 carbonhydrate;
    
    	public static class Builder {
    		private final int servingSize;
    		private final int servings;
    
    		private int calories = 0;
    		private int fat = 0;
    		private int sodium = 0;
    		private int carbonhydrate = 0;
    		
    		public Builder(int servingSize,int serving){
    			this.servingSize = servingSize;
    			this.servings = serving;
    		}
    		
    		public Builder calories(int val){
    			this.calories = val;
    			return this;
    		}
    		
    		public Builder fat(int val){
    			this.fat = val;
    			return this;
    		}
    		
    		public Builder carbonhyate(int val){
    			this.carbonhydrate = val;
    			return this;
    		}
    		
    		public Builder sodium(int val){
    			this.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;
    		carbonhydrate = builder.carbonhydrate;
    	}
    	
    	public static void main(String[] args) {
    		NutritionFacts cocacola = new NutritionFacts.Builder(240,80).calories(100).sodium(35).carbonhyate(27).build();
    	}
    }
     
  • 相关阅读:
    百度地图定位经纬度返回4.9E-324有关问题
    急!JDBC问题,发生通信错误。错误位置:Reply.fill()。消息:数据不足。 ERRORCODE=-4499, SQLSTATE=08001
    Android 图片添加水印图片或者文字
    查询出多条记录,取最大或最小的某条件的记录,取唯一记录
    listView获取item的Edit内容,listView中的edit内容在滚动时自动赋值了前面的内容
    ListView中的item的按照和item点击事件并存
    update select
    db2 怎么计算两个时间相差多少个月。如2015-10-10 和2014-1-12
    解决AndroidADT自带Eclipse编辑器不能自动代码提示的问题
    python协程详解,gevent asyncio
  • 原文地址:https://www.cnblogs.com/luckyliu/p/2389304.html
Copyright © 2011-2022 走看看