1、考慮使用靜態(tài)生產(chǎn)方法替代構(gòu)造方法
如果某一個類擁有多個構(gòu)造方法,且參數(shù)各不相同,使用靜態(tài)的生產(chǎn)方法替代構(gòu)造方法的好處:
- 過多的構(gòu)造方法增加了使用者對于類的理解成本,并且很容易產(chǎn)生誤解;
- 靜態(tài)生產(chǎn)方法擁有自己的方法名,可以進(jìn)一步描述該方法的使用場景和參數(shù)意義;
- 靜態(tài)生產(chǎn)方法封裝了內(nèi)部的實現(xiàn),便于擴(kuò)展。例如實現(xiàn)內(nèi)部的單例,緩存,子類,匿名類,簡單工廠等等;
缺點:
- 如果構(gòu)造方法是私有的,僅僅使用靜態(tài)生產(chǎn)方法,子類不能繼承構(gòu)造方法;
- 如果使用靜態(tài)方法,需要使用者首先了解靜態(tài)生產(chǎn)方法的存在,并在所有的靜態(tài)方法中尋找需要的方法,所以建議生成JavaDoc文檔,并方法名遵循特定的命名規(guī)則。例如添加方便識別的方法名前綴:from、of、new、get、create;
總結(jié):使用靜態(tài)方法替代構(gòu)造方法的好處在于,解耦使用者和構(gòu)造方法的強(qiáng)引用關(guān)系,靜態(tài)方法作為中間層方便功能的擴(kuò)展;
代碼示例:
// java.math.BigInteger
// 通過不同方法名,進(jìn)一步區(qū)分構(gòu)造函數(shù)之間的區(qū)別
public static BigInteger probablePrime(int bitLength, @NonNull Random random) {
return new BigInteger(bitLength, 100, random);
}
public BigInteger remainder(@NonNull BigInteger divisor) {
BigInt remainder = new BigInt();
BigInt.division(getBigInt(), divisor.getBigInt(), null, remainder);
return new BigInteger(remainder);
}
// android.os.Message
// 使用靜態(tài)生產(chǎn)方法替換了構(gòu)造方法,obtain內(nèi)部實現(xiàn)了Message對象緩存復(fù)用
public static Message obtain() {}
public static Message obtain(Message orig){}
public static Message obtain(Handler h) {}
public static Message obtain(Handler h, int what) {}
// 內(nèi)部實現(xiàn)了單例
// java.lang.Boolean
public static Boolean valueOf(boolean b) {}
// java.lang.String
public static String valueOf(boolean b) {}
2、考慮使用Builder替代構(gòu)造方法
當(dāng)一個Class擁有非常多的屬性,擁有多個構(gòu)造方法,且大部分屬性都是可選的,可以考慮使用Builder替代構(gòu)造方法。此方案的優(yōu)點:
- 使用者只關(guān)注需要的屬性即可,讓構(gòu)造方法的參數(shù)在控制范圍內(nèi);
- 相對于setter方法的使用,鏈?zhǔn)秸{(diào)用的結(jié)構(gòu)可讀性更好;
- 便于添加和修改屬性的默認(rèn)值;
缺點:
- Builder中的屬性要和Class的屬性個數(shù)可能相同,導(dǎo)致代碼冗余,開發(fā)成本增加;
- 容易漏掉必填參數(shù),可以通過在Builder的構(gòu)造方法中添加必填參數(shù);
總結(jié):Builder是在適當(dāng)增加開發(fā)成本的基礎(chǔ)上,讓擁有過多參數(shù)的類的創(chuàng)建更加靈活可控,并且Builder解耦了使用者和構(gòu)造方法之間的強(qiáng)引用關(guān)系,便于擴(kuò)展。另外要注意Builder只是中間件,不適合添加和Class屬性密切相關(guān)的邏輯,應(yīng)該添加在Class的setter方法中,保持調(diào)用Builder中的setter和Class中的setter的一致性;
代碼示例:
AlertDialog.Builder(this)
.setIcon()
.setTitle("")
.setMessage("")
.setView()
.setCancelable()
.... // more setter
.create();
3、確保單例屬性的構(gòu)造方法是私有的或是枚舉類型
如何防止單例模式被破壞:
- 在構(gòu)造方法中判斷,如果已經(jīng)instance已經(jīng)創(chuàng)建,拋出異常;
- 單例需要被序列化,可以在類中添加private Object readResolve()方法,返回instance;
- 使用枚舉類型實現(xiàn)單例;
枚舉類型實現(xiàn)單例的好處:
- 自動支持序列化;
- 有效防止單例模式被破壞;
枚舉實現(xiàn)單例的缺點:
- 不能繼承和被繼承;
- 一旦需求更改不再使用單例,修改成本較大;
4、確保不能被實例化的類構(gòu)造方法是私有的
如果一個類只是作為工具來使用,他只擁有靜態(tài)屬性和靜態(tài)方法,那么確保它的構(gòu)造方法是私有的,防止使用者通過實例化錯誤的使用該類。
5、避免依賴注入硬編碼
如果實現(xiàn)一個類,內(nèi)部使用了某單例或某靜態(tài)工具類,應(yīng)該避免依賴硬編碼,使用構(gòu)造函數(shù)傳參或工廠模式更為靈活。
代碼案例:
// 代碼1,SpellChecker內(nèi)部直接創(chuàng)建Lexicon,導(dǎo)致兩者強(qiáng)耦合
public class SpellChecker {
// 直接創(chuàng)建Lexicon實例,
private static final Lexicon dictionary = ...;
// 私有構(gòu)造方法
private SpellChecker() {} // Noninstantiable
public static boolean isValid(String word) { ... }
public static List<String> suggestions(String typo) { ... }
}
// 代碼2,Lexicon的賦值由構(gòu)造參數(shù)決定,兩者是間隔耦合的關(guān)系,外部可以調(diào)整Lexicon的實現(xiàn)(接口或繼承)
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}
6、避免創(chuàng)建不必要的對象
如果一個對象能夠復(fù)用,那就盡可能復(fù)用,這樣可以避免對象頻繁的創(chuàng)建引起的內(nèi)存抖動,提高程序運行的效率。例如以下場景:
- 字符串盡量不要使用new String(),new表示強(qiáng)制創(chuàng)建一個對象,而不是復(fù)用常量池;
- 固定不變的參數(shù)對象,應(yīng)該保持單例,不要每次傳參都new;
- 避免裝箱和拆箱;
- 提供工廠方法,內(nèi)部實現(xiàn)對象復(fù)用,防止使用者創(chuàng)建不必要的對象;
不要過度的緩存對象,這樣會破壞JVM的內(nèi)存回收機(jī)制,導(dǎo)致不能正常回收,所以應(yīng)該復(fù)用重量級的對象(使用頻繁,內(nèi)存較大等),而不是考慮所有的對象。