天天看點

Effective Java——建立和銷毀對象

靜态工廠方法相對構造器的優勢:

  • 靜态工廠方法有名稱。一個類隻有一個帶有指定簽名的構造器。由于靜态工廠方法有名稱,是以不受限制。當一個類需要多個帶有相同簽名的構造器時,就用靜态工廠方法代替構造器,并選擇合适名稱突出差別。
  • 靜态工廠方法不必在每次調用的時候都建立一個新對象。靜态工廠方法能為重複的調用傳回相同的對象,有助于類能控制在某個時刻哪些執行個體應該存在。[這種類稱為執行個體控制的類]
  • 靜态工廠方法可以傳回原傳回類型的任何子類型的對象。
public interface Service {
    //........
}

public interface Provider {
    Service newService();
}

public class Services {
    private Services(){}
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<Jenny>";
    public static void registerDefultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }
    public static void registerProvider(String name, Provider p){
        providers.put(name, p);
    }
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name){
        Provider p = providers.get(name);
        if(p == null){
            throw new IllegalArgumentException("no provider registered with name: "+name);
        }
        return p.newService();
    }
}
           
  • 靜态工廠方法在建立參數化執行個體的時候使代碼變得更簡潔。但是,在調用參數化類的構造器時,即使類型參數很明顯,也必須指明。這需要兩次提供類型參數:
Map<String,List<String>> m = new HashMap<String,List<String>>();

//eg:HashMap提供了這個靜态工廠方法
public static <k, v> HashMap<k, v> newInstance(){
    return new HashMap<k, v>();
}

//就可以通過以下代碼替換上面繁瑣的聲明
Map<String,List<String>> m = HashMap.newInstance();
           

靜态工廠方法的缺點:

  • 類如果不含有公有的或受保護的構造器,就不能被子類化。
  • 與其他的靜态方法沒有差別。

遇到多個構造器參數時,要用建構器

eg.多個構造器

public class NutritrionFats {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;

    public NutritrionFats(int servingSize, int servings){
        this(servingSize, servings, );
    }

    public NutritrionFats(int servingSize, int servings, int calories){
        this(servingSize, servings, calories, );
    }

    public NutritrionFats(int servingSize, int servings, int calories, int fat){
        this(servingSize, servings, calories, fat, );
    }

    public NutritrionFats(int servingSize, int servings, int calories, int fat, int sodium){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
    }
}
           

JavaBean 建構器

public class NutritrionFats {
    private int servingSize = -;
    private int servings = -;
    private int calories = ;
    private int fat = ;
    private int sodium = ;

    public NutritrionFats(){ }

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

這種方式雖然彌補了重疊構造器的不足,但是,JavaBean自身存在不足,因構造過程被分到幾個調用中,在構造過程中JavaBean可能處于不一緻的狀态。類無法通過構造參數的有效性來保證一緻性。

下面是Builder模式來完善以上:

public class NutritrionFats {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;

    public static class Builder{
        private final int servingSize;
        private final int servings;

        private int calories = ;
        private int fat = ;
        private int sodium = ;
        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 sodium(int val){
            sodium = val;
            return this;
        }
        public NutritrionFats build(){
            return new NutritrionFats(this);
        }
    }

    private NutritrionFats (Builder build){
        servingSize = build.servingSize;
        servings = build.servings;
        calories = build.calories;
        fat = build.fat;
        sodium = build.sodium;
    }
}
           

注意:NutritrionFats是不可變的,所有的預設參數都放在一個地方,builder的setter方法傳回builder本身,以便把調用連接配接起來。以下是調用用戶端的代碼: