Effective Java 讀書筆記
阿新 • • 發佈:2018-12-09
第二章 建立與銷燬物件
1、考慮用靜態工廠方法代替構造器
優點:
1、有名稱
2、不必在每次呼叫他們的時候都建立一個新的物件
3、他們可以返回原返回型別的任何子型別物件
4、使程式碼更簡潔
缺點:
1、類如果不含公有的或者受保護的構造器,就不能被子類化
2、與其他的靜態方法實際上沒有任何區別,不能一眼就看出來
可以用一些慣用的名稱:
valueOf
Of
getInstance
newInstance
getType
NewType
關於第三條優點,這種靈活的靜態工廠方法可以構成 服務者提供框架(Service Provider Framework)
/** * 服務提供者框架 * 多個服務提供者實現一個服務,系統為服務提供者的客戶端提供多個實現,並把他們從多個實現中解耦出來 */ public class Services { private Services(){ } private static final String DEFAULT_PROVIDER = "DEFAULT_PROVIDER"; private static final ConcurrentHashMap<String,Provider> providers = new ConcurrentHashMap(); /** * 註冊提供者 * @param name * @param provider */ public static void registProvider(String name,Provider provider){ providers.put(name,provider); } /** * 註冊預設提供者 * @param provider */ public static void registDefaultProvider(Provider provider){ providers.put(DEFAULT_PROVIDER,provider); } /** * 提供預設服務 * @return */ public static Service newInstance(){ return newInstance(DEFAULT_PROVIDER); } /** * 根據相應條件提供特定服務 * @param name * @return */ public static Service newInstance(String name){ Provider provider = providers.get(name); if(provider == null) throw new IllegalArgumentException("服務不存在"); return provider.newService(); } /** * 服務提供者介面 */ interface Provider{ /** * 例項化一個服務 * @return */ Service newService(); /** * 註冊提供者 */ void registProvider(); } /** * 服務介面 */ interface Service{ default void say(){ System.out.println("i am default"); } } }
/** * 服務A */ public class ServiceA implements Services.Service{ public void say(){ System.out.println("i am A"); } /** * 服務提供者,負責註冊服務,構建服務類 */ static class ProviderA implements Services.Provider{ public final static String NAME = "A"; @Override public Services.Service newService() { return new ServiceA(); } @Override public void registProvider() { Services.registProvider(NAME,this); } } } /** * 服務B */ public class ServiceB implements Services.Service{ public void say(){ System.out.println("i am B"); } /** * 服務提供者,負責構建服務 向Services類註冊服務 */ static class ProviderB implements Services.Provider{ public final static String NAME = "B"; @Override public Services.Service newService() { return new ServiceB(); } @Override public void registProvider() { Services.registProvider(NAME,this); } } }
一個測試例子
public class Client {
public static void main(String[] args){
Services.Provider providerA = new ServiceA.ProviderA();
Services.Provider providerB = new ServiceB.ProviderB();
providerA.registProvider();
providerB.registProvider();
Services.Service service = Services.newInstance("B");
service.say();
}
}
最終列印
i am B
2、遇到多個構造器引數時要考慮用構建器
重疊構造器模式可行,但是當有許多引數的時候,客戶端程式碼會很難編寫,過多的構造器也不利於閱讀
使用構建器,優點
1、客戶端程式碼更加清晰,利於閱讀
2、可以在set方法或者build中對傳入引數進行驗證,及時丟擲異常
一個簡單的例子
/**
* 不可變類,構造器私有
*/
public class Service {
private final String name;
private final int age;
private final int height;
private final int weight;
/**
* 內部構建器,通過構建器例項化類
*/
public static class Bulider{
private final String name;
private int age;
private int height;
private int weight;
public Bulider(String name){
this.name = name;
}
public Bulider setAge(int age){
this.age = age;
return this;
}
public Bulider setHeight(int height){
this.height = height;
return this;
}
public Bulider setWeight(int weight){
this.weight = weight;
return this;
}
public Service bulider(){
return new Service(this);
}
}
public String toString(){
return "name:"+name+" age:"+age+" height:"+height+" weight:"+weight;
}
private Service(Bulider bulider){
this.name = bulider.name;
this.age = bulider.age;
this.height = bulider.height;
this.weight = bulider.weight;
}
public static void main(String[] args){
Service service = new Bulider("張三")
.setAge(10).setHeight(170).setWeight(120).bulider();
System.out.println(service.toString());
}
}