【Effective Java】第二章:静态工厂、构建器、强化Singleton属性、私有构造器、
生活随笔
收集整理的這篇文章主要介紹了
【Effective Java】第二章:静态工厂、构建器、强化Singleton属性、私有构造器、
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 一. 用靜態工廠方法代替構造器
-
-
- 優勢:
- 劣勢:
- 實例代碼:
-
- 二. 遇到多個構造器參數時要考慮使用構建器
-
-
- ① 重疊構建器
- ② JavaBeans模式
- ③ Builder模式
-
- 三. 用私有構造器或枚舉類型強化Singleton屬性
-
-
- 方法一:公有靜態成員是個final域
- 方法二:公有的成員是個靜態工廠
- 方法三:一個包含單個元素的枚舉類型
-
- 四. 通過私有構造器強化不可實例化的能力
一. 用靜態工廠方法代替構造器
優勢:
- 有名稱,可以確切地描述正被返回的對象。
- 不必在每次調用的時候都創建新對象。
- 可以返回原返回類型的任何子類型對象。
- 返回的對象的類可以隨著每次調用而產生變化。
- 返回的對象所屬的類,在編寫靜態工廠方法時可以不存在。
劣勢:
- 類如果不含公有的或者受保護的構造器,就不能被子類化。
- 程序員很難發現他們,但是可以通過遵守標準的命名習慣來彌補。
實例代碼:
這是第二版的代碼,在第三版里已經刪去了。不過可以用來便于理解。
// 英文部分可以不看,或者作為參考。/**
用于描述Provider所提供的服務
*/
public interface Service {
... // Service-specific methods go here
}
/**
用于描述Provider,每個Provider都要有自己的newService
*/
public interface Provider {
Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
// 私有化構造方法,不會把工廠實例化,直接用類名。
private Services() { } // Prevents instantiation (Item 4)// 使用Map來存儲各provider及其name的映射。
// 使用final:只是providers一直指向當前HashMap,對HashMap里的內容變化沒有影響。
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
ITEM 1: CONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS 9
// Provider registration API
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
// 注冊Provider到工廠Services里。
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
// Service access API
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
// 根據name從工廠中取出對應Provider提供的服務。
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();
}
}
二. 遇到多個構造器參數時要考慮使用構建器
① 重疊構建器
參考下面的代碼,活用this,但是參數多的時候難寫,并且用起來容易出錯。
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);
}
12 CHAPTER 2 CREATING AND DESTROYING OBJECTS
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 = carbohydrate;
}
}
② JavaBeans模式
- 構造函數就是:ClassName(){}
- 把賦值交給Setters來實現
- 彌補了重疊構造器的不足,容易創建實例,并且易讀
- 缺點1:構造過程中JavaBean可能處于不一致的狀態(畢竟要多次賦值)
- 缺點2:使得把類做成不可變的可能性不存在(不能final,畢竟之后還要set的嘛)
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() { }
ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 13
// 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; }
}
③ Builder模式
- 既保證了像重疊構造器的安全性,也保證像JavaBeans的可讀性
- 描述:用類的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;
// 在構造函數前,先寫好內部類Builder
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
// 與類的final不同,此處因為要用setters,因此不設為final。
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;
}
// 逐個屬性設定值,每次都return this:和之后的具體調用相關
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; }
// build():使用this builder作為參數來構造類的對象。
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
// 私有的構造函數,使用已經完全初始化的builder來進行唯一的一次初始化。
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
- 還適用于類層次結構(這里會麻煩點,涉及到抽象類、范式等等)
// 先來個抽象的父類Pizza!
// Builder pattern for class hierarchies
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
// Builder也是抽象的,這里用到T進行一個約束:只能是Pizza.Builder的字類
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
// 這里不能return T,所以先自己寫一個self()函數來代替(返回還未構成的類)
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
// <?>:只要是繼承了Builder的參數都可用
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}// 再來兩個子類披薩
// 重寫之前的抽象函數,并且構造函數中要有super(builder)
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder<Builder> {
private boolean sauceInside = false; // Default
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override public Calzone build() {
return new Calzone(this);
}
@Override protected Builder self() { return this; }
}
private Calzone(Builder builder) {
super(builder);
sauceInside = builder.sauceInside;
}
}// 然后實際使用的時候
// 使用類名建builder,一直builder下去,最后讓builder來build對象出來。
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
Calzone calzone = new Calzone.Builder()
.addTopping(HAM).sauceInside().build();
三. 用私有構造器或枚舉類型強化Singleton屬性
- 單例模式
方法一:公有靜態成員是個final域
// Singleton with public final field
public class Elvis {
// 公有 靜態 final
public static final Elvis INSTANCE = new Elvis();
// 構造函數私有化
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
方法二:公有的成員是個靜態工廠
相對于方法一,畢竟類的成員還是要用方法來訪問比較好。
// Singleton with public final field
public class Elvis {
// 把public 變成 private
private static final Elvis INSTANCE = new Elvis();
// 構造函數私有化
private Elvis() { ... }
// 公有靜態方法,返回唯一的實例。
public static Elvis getInstance(){return INSTANCE}
public void leaveTheBuilding() { ... }
}
方法三:一個包含單個元素的枚舉類型
- Java的enum和C++的差別很大。
- 這樣子寫會比較簡便。
// Enum singleton - the preferred approach
public enum Elvis{// 只有一個“實例”INSTANCE;public void leaveTheBuilding(){...}
}
四. 通過私有構造器強化不可實例化的能力
- 這塊感覺沒啥好講的,就是給構造函數加個private。
- 好壞處之類的可以參考書里的內容。
總結
以上是生活随笔為你收集整理的【Effective Java】第二章:静态工厂、构建器、强化Singleton属性、私有构造器、的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过此注释改善您的JUnit体验
- 下一篇: jdeveloper_JDevelope