一、編程規(guī)范
(一)命名規(guī)范:(命名要望文知意,不要嫌長)
-
包名要統(tǒng)一小寫
-
類名、接口名遵從駝峰式命名,DO / BO /DTO / VO / AO/PO例外
例子:MarcoPolo / UserDO / XmlService / TcpUdpDeal
-
方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用 lowerCamelCase 風(fēng)格
例子:localValue / getHttpMessage() / inputUserId
-
常量命名全部大寫,單詞間下劃線隔開
例子:MAX_STOCK_COUNT
-
枚舉類名要以Enum為后綴,且枚舉成員名稱要全大寫,和常量命名一樣
例子:枚舉類名 ProcessStatusEnum 枚舉成員名稱 SUCCESS/UNKOWN_REASON
-
Service/DAO層方法命名:
-
獲取單個對象的方法用get做前綴
-
獲取多個對象的方法用list做前綴
-
獲取統(tǒng)計值的方法用count做前綴
-
插入的方法用save/insert做前綴
-
刪除的方法用remove/delete做前綴
-
修改的方法用update做前綴
-
領(lǐng)域模型命名
-
查詢數(shù)據(jù)庫對象:xxxPO
-
業(yè)務(wù)邏輯層對象:xxxBO
-
接收客戶端參數(shù)的對像:xxxVO
-
層級之間的數(shù)據(jù)傳輸對象:xxxDTO
(二)常量定義
-
不允許任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。常量的復(fù)用層次有五層:跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類內(nèi)共享常量
-
跨應(yīng)用共享常量:放置在二方庫中,通常是client.jar中的constant目錄下
-
應(yīng)用內(nèi)共享常量:放置在一方庫中,通常是modules中的constant目錄下
-
子工程內(nèi)部共享常量:即在當(dāng)前子工程的constant目錄下
-
包內(nèi)共享常量:即在當(dāng)前包下單獨的constant目錄下
-
類內(nèi)共享常量:直接在類內(nèi)部private static final定義
-
如果變量值在一個范圍內(nèi)變化,且?guī)в忻Q之外的延展屬性,定義為枚舉類
例子:數(shù)字就是延伸信息,標(biāo)識星期幾
public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6),SUNDAY(7);}
(三)代碼格式
-
方法體內(nèi)的執(zhí)行語句組、變量的定義語句組、不同的業(yè)務(wù)邏輯之間或者不同的語義之間插入一個空行。
-
單行字符數(shù)限制不超過 120 個,超出需要換行,換行時遵循如下原則:
-
第二行相對第一行縮進(jìn) 4 個空格,從第三行開始,不再繼續(xù)縮進(jìn),參考示例。
-
運算符與下文一起換行。
-
方法調(diào)用的點符號與下文一起換行。
-
4方法調(diào)用時,多個參數(shù),需要換行時,在逗號后進(jìn)行。
-
在括號前不要換行
-
適當(dāng)加空格進(jìn)行格式整理(idea使用用快捷鍵ctrl+alt+L快速整理)
(四)OOP規(guī)范
-
所有覆寫的方法,必須加@Override注解。可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象中對方法簽名進(jìn)行修改,其實現(xiàn)類會馬上編譯報錯
-
外部正在調(diào)用或者二方庫依賴的接口,不允許修改方法簽名,避免對接口調(diào)用方產(chǎn)生影響。接口過時必須加@Deprecated 注解,并清晰地說明采用的新接口或者新服務(wù)是什么。
-
調(diào)用equals方法比較時,應(yīng)使用常量或確定有值的對象來調(diào)用equals方法,并且包裝類對象之間的比較,全部使用 equals 方法比較
-
關(guān)于基本數(shù)據(jù)類型和包裝數(shù)據(jù)類型的使用標(biāo)準(zhǔn):
-
所有的POJO類屬性必須使用包裝數(shù)據(jù)類型
-
RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型
-
所有的局部變量推薦使用基本數(shù)據(jù)類型
-
構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請放在 init 方法中
-
類內(nèi)方法定義順序依次是:公有方法或保護(hù)方法 > 私有方法 > getter/setter方法。
說明:公有方法是類的調(diào)用者和維護(hù)者最關(guān)心的方法,首屏展示最好;保護(hù)方法雖然只是子類關(guān)心,也可能是“模板設(shè)計模式”下的核心方法;而私有方法外部一般不需要特別關(guān)心,是一個黑盒實現(xiàn);因為承載的信息價值較低,所有 Service 和 DAO 的 getter/setter 方法放在類體
-
循環(huán)體內(nèi),字符串的連接方式,使用 StringBuilder 的 append 方法進(jìn)行擴(kuò)展。
說明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會 new 出一個 StringBuilder 對象,然后進(jìn)行append 操作,最后通過 toString 方法返回 String 對象,造成內(nèi)存資源浪費
-
類成員與方法訪問控制從嚴(yán):
-
如果不允許外部直接通過 new 來創(chuàng)建對象,那么構(gòu)造方法必須是 private。
-
工具類不允許有 public 或 default 構(gòu)造方法。
-
類非 static 成員變量并且與子類共享,必須是 protected。
-
類非 static 成員變量并且僅在本類使用,必須是 private。
-
類 static 成員變量如果僅在本類使用,必須是 private。
-
若是 static 成員變量,必須考慮是否為 final。
-
類成員方法只供類內(nèi)部調(diào)用,必須是 private。
-
類成員方法只對繼承類公開,那么限制為 protected。
說明:任何類、方法、參數(shù)、變量,嚴(yán)控訪問范圍。過于寬泛的訪問范圍,不利于模塊解耦
(五)集合處理
-
在 subList 場景中,高度注意對原集合元素個數(shù)的修改,會導(dǎo)致子列表的遍歷、增加、刪除均會產(chǎn)生 ConcurrentModificationException 異常。并且不要在 foreach 循環(huán)里進(jìn)行元素的 remove/add 操作。remove 元素請使用 Iterator方式,如果并發(fā)操作,需要對 Iterator 對象加鎖。
//正例:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (刪除元素的條件) {
iterator.remove();
}
}
//反例:
List list = new ArrayList();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
-
使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的 toArray(T[] array),傳入的是類型完全一樣的數(shù)組,大小就是 list.size()。
說明:使用 toArray 帶參方法,入?yún)⒎峙涞臄?shù)組空間不夠大時,toArray 方法內(nèi)部將重新分配
內(nèi)存空間,并返回新數(shù)組地址;如果數(shù)組元素大于實際所需,下標(biāo)為[ list.size() ]的數(shù)組
元素將被置為 null,其它數(shù)組元素保持原值,因此最好將方法入?yún)?shù)組大小定義與集合元素
個數(shù)一致。
-
使用工具類 Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時,不能使用其修改集合相關(guān)的方法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException 異常。
說明:asList 的返回對象是一個 Arrays 內(nèi)部類,并沒有實現(xiàn)集合的修改方法。Arrays.asList體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺的數(shù)據(jù)仍是數(shù)組。
-
泛型通配符<? extends T>來接收返回的數(shù)據(jù),此寫法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,做為接口調(diào)用賦值時易出錯。
說明:擴(kuò)展說一下 PECS(Producer Extends Consumer Super)原則:第一、頻繁往外讀取內(nèi)容的,適合用<? extends T>。第二、經(jīng)常往里插入的,適合用<? super T>。
/**
* ? extends T 表示T或T的子類
* ? super T 表示T或T的父類
* ? 表示可以是任意類型
**/
class Gent<T> {
public void test(){
System.out.println("gent");
}
}
class SupC {
public void test(){
System.out.println("supC");
}
}
class Bc extends SupC {
//入?yún)⒅荒苁荢upC或SupC的子類
public void testExtends(gent<? extends SupC> o){
System.out.println("Bc");
}
//入?yún)⒅荒苁荁c或Bc的父類
public void testSuper(gent<? super Bc> o){
System.out.println("Bc");
}
//入?yún)⒖梢允侨我忸愋?
public void testSuper(gent<?> o){
System.out.println("gent");
}
}
-
合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負(fù)面影響。
說明:有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。穩(wěn)定性指集合每次遍歷的元素次序是一定的。如:ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是order/sort。
-
利用 Set 元素唯一的特性,可以快速對一個集合進(jìn)行去重操作,避免使用 List 的contains 方法進(jìn)行遍歷、對比、去重操作
//兩個Set比較找出交集、差集、并集
public static void setCompare() {
Set<Integer> result = new HashSet<Integer>();
Set<Integer> set1 = new HashSet<Integer>() {{
add(1);
add(3);
add(4);
}};
System.out.println("set1 = " + set1.toString());
Set<Integer> set2 = new HashSet<Integer>() {{
add(1);
add(2);
add(3);
}};
System.out.println("set2 = " + set2.toString());
//交集:set1和set2相同的元素
result.clear();
result.addAll(set1);
result.retainAll(set2);
System.out.println("交集:" + result);
//result結(jié)果:[1, 3]
//差集:元素存在set1,但不存在set2
result.clear();
result.addAll(set1);
result.removeAll(set2);
System.out.println("差集:" + result);
//result結(jié)果:[4]
//并集:set1的set2的元素之和
result.clear();
result.addAll(set1);
result.addAll(set2);
System.out.println("并集:" + result);
//result結(jié)果:[1, 2, 3, 4]
}
(六)并發(fā)處理
-
高并發(fā)時,同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖;能鎖區(qū)塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
說明:盡可能使加鎖的代碼塊工作量盡可能的小,避免在鎖代碼塊中調(diào)用 RPC 方法。
-
并發(fā)修改同一記錄時,避免更新丟失,需要加鎖。要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫層使用樂觀鎖,使用 version 作為更新依據(jù)。
說明:如果每次訪問沖突概率小于 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數(shù)不得小于 3 次。
-
避免 Random 實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導(dǎo)致的性能下降。
說明:Random 實例包括 java.util.Random 的實例或者 Math.random()的方式。正例:在 JDK7 之后,可 以直接使用 API ThreadLocalRandom,而在 JDK7 之前,需要編碼保證每個線程持有一個實例。
-
HashMap 在容量不夠進(jìn)行 resize 時由于高并發(fā)可能出現(xiàn)死鏈,導(dǎo)致 CPU 飆升,在開發(fā)過程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來規(guī)避此風(fēng)險
(七)控制語句
-
在一個 switch 塊內(nèi),每個 case 要么通過 break/return 等來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個 case 為止;在一個 switch 塊內(nèi),都必須包含一個 default 語句并且放在最后,即使它什么代碼也沒有。
-
在 if/else/for/while/do 語句中必須使用大括號。即使只有一行代碼,避免采用單行的編碼方式:if (condition) statements;
-
表達(dá)異常的分支時,少用 if-else 方式,這種方式可以改寫成:if (condition) {...return obj;}// 接著寫 else 的業(yè)務(wù)邏輯代碼;
說明:如果非得使用 if()...else if()...else...方式表達(dá)邏輯,避免后續(xù)代碼維護(hù)困難,請勿超過 3 層。
正例:超過 3 層的 if-else 的邏輯判斷代碼可以使用衛(wèi)語句、策略模式、狀態(tài)模式等來實現(xiàn),
衛(wèi)語句:
//改造前(看著亂,不清楚每個邏輯分支的具體條件)
//當(dāng) a等于1
if (a == 1){
//當(dāng) b等于2
if (b == 2){
...
} else {
//當(dāng) b不等于1
//c等于3
if (c == 3){
...
}
}
}else{
//當(dāng) a不等于1
//當(dāng) d等于4
if(d == 4){
...
}
}
//改造后(從上到下,看著清晰明了,并清楚每個邏輯分支對應(yīng)的條件)
//情況一:當(dāng) a等于1 并 b等于2
if(a == 1&&b == 2){
...
}
//情況二:當(dāng) a等于1 并 b不等于2 并 c等于3
if(a == 1&&b != 2&&c == 3){
...
}
//情況三:當(dāng) a不等于1 并 d等于4
if(a != 1&&d == 4){
...
}
策略模式:
/**
* 實現(xiàn)不同動物發(fā)出不同聲音
* 如:狗--汪汪 貓--喵喵 牛--哞哞 羊--咩咩
* @author gz
* */
//改造前 (后期加多種動物類型,每次都需要修改共用的邏輯,易出錯,且不易維護(hù))
public class AnimalCryTest {
private static final String DOG = "狗";
private static final String CAR = "貓";
private static final String CATTLE = "牛";
private static final String SHEEP = "羊";
/**
* 動物的叫聲
* @param animalName 動物名稱
*/
public static void animalCry(String animalName){
if (DOG.equals(animalName)){
System.out.println(animalName+"----汪汪");
}else if (CAR.equals(animalName)){
System.out.println(animalName+"----喵喵");
}else if (CATTLE.equals(animalName)){
System.out.println(animalName+"----哞哞");
}else {
System.out.println(animalName+"----咩咩");
}
}
public static void main(String[] args) {
// 狗--汪汪
animalCry(DOG);
// 貓--喵喵
animalCry(CAR);
// 牛--哞哞
animalCry(CATTLE);
// 羊--咩咩
animalCry(SHEEP);
}
}
//改造后(后期加多種動物類型,只需要實現(xiàn)對應(yīng)功能的接口,各個邏輯是相互獨立的,不易出錯,易維護(hù))
/**
* 1、定義一個接口
**/
interface Animal {
/**
* 動物叫聲
*/
void animalCry();
}
/**
* 2、創(chuàng)建實現(xiàn)該接口的實現(xiàn)類
**/
class Dog implements Animal {
@Override
public void animalCry() {
System.out.println("狗----汪汪");
}
}
class Car implements Animal {
@Override
public void animalCry() {
System.out.println("貓----喵喵");
}
}
class Cattle implements Animal {
@Override
public void animalCry() {
System.out.println("牛----哞哞");
}
}
class Sheep implements Animal {
@Override
public void animalCry() {
System.out.println("羊----咩咩");
}
}
/**
* 3、多態(tài)性質(zhì),根據(jù)實例化對象的不同,調(diào)用實例化對象對應(yīng)的具體方法
**/
public class AnimalCryTest {
public static void main(String[] args) {
// 創(chuàng)建狗的實例對象,并調(diào)用其對應(yīng)的animalCry方法 狗--汪汪
Animal dog = new Dog();
dog.animalCry();
// 創(chuàng)建貓的實例對象,并調(diào)用其對應(yīng)的animalCry方法 貓--喵喵
Animal car = new Car();
car.animalCry();
// 創(chuàng)建牛的實例對象,并調(diào)用其對應(yīng)的animalCry方法 牛--哞哞
Animal cattle = new Cattle();
cattle.animalCry();
// 創(chuàng)建羊的實例對象,并調(diào)用其對應(yīng)的animalCry方法 羊--咩咩
Animal sheep = new Sheep();
sheep.animalCry();
}
}
狀態(tài)模式:
/**
* 在"投了25分錢"的狀態(tài)下"轉(zhuǎn)動曲柄",會售出糖果;而在"沒有25分錢"的狀態(tài)下"轉(zhuǎn)動曲柄"會提示請先投幣。
* 四個狀態(tài):
* 1、沒有硬幣狀態(tài)
* 2、投幣狀態(tài)
* 3、出售糖果狀態(tài)
* 4、糖果售盡狀態(tài)
* 四個動作:
* 1、投幣
* 2、退幣
* 3、轉(zhuǎn)動出糖曲軸
* 4、發(fā)糖
* @author gz
*/
//改造前
public class NoPatternGumballMachine{
/*
* 四個狀態(tài)
*/
/**沒有硬幣狀態(tài)*/
private final static int NO_QUARTER = 0;
/**投幣狀態(tài)*/
private final static int HAS_QUARTER = 1;
/**出售糖果狀態(tài)*/
private final static int SOLD = 2;
/**糖果售盡狀態(tài)*/
private final static int SOLD_OUT = 3;
private int state = SOLD_OUT;
private int candyCount = 0;
public NoPatternGumballMachine(int count) {
this.candyCount = count;
if(candyCount > 0)
state = NO_QUARTER;
}
/*
* 四個動作
*/
/**
* 投幣
*/
public void insertQuarter() {
if(NO_QUARTER == state){
System.out.println("投幣");
state = HAS_QUARTER;
}
else if(HAS_QUARTER == state){
System.out.println("請不要重復(fù)投幣!");
returnQuarter();
}
else if(SOLD == state){
System.out.println("已投幣,請等待糖果");
returnQuarter();
}else if(SOLD_OUT == state){
System.out.println("糖果已經(jīng)售盡");
returnQuarter();
}
}
/**
* 退幣
*/
public void ejectQuarter() {
if(NO_QUARTER == state){
System.out.println("沒有硬幣,無法彈出");
}
else if(HAS_QUARTER == state){
returnQuarter();
state = NO_QUARTER;
}
else if(SOLD == state){
System.out.println("無法退幣,正在發(fā)放糖果,請等待");
}else if(SOLD_OUT == state){
System.out.println("沒有投幣,無法退幣");
}
}
/**
* 轉(zhuǎn)動出糖曲軸
*/
public void turnCrank() {
if(NO_QUARTER == state){
System.out.println("請先投幣");
}
else if(HAS_QUARTER == state){
System.out.println("轉(zhuǎn)動曲軸,準(zhǔn)備發(fā)糖");
state = SOLD;
}
else if(SOLD == state){
System.out.println("已按過曲軸,請等待");
}else if(SOLD_OUT == state){
System.out.println("糖果已經(jīng)售盡");
}
}
/**
* 發(fā)糖
*/
public void dispense() {
if(NO_QUARTER == state){
System.out.println("沒有投幣,無法發(fā)放糖果");
}
else if(HAS_QUARTER == state){
System.out.println("this method don't support");
}
else if(SOLD == state){
if(candyCount > 0){
System.out.println("分發(fā)一顆糖果");
candyCount --;
state = NO_QUARTER;
}
else{
System.out.println("抱歉,糖果已售盡");
state = SOLD_OUT;
}
}else if(SOLD_OUT == state){
System.out.println("抱歉,糖果已售盡");
}
}
/**
* 退還硬幣
*/
protected void returnQuarter() {
System.out.println("退幣……");
}
}
//改造后
//1、定義一個接口或者抽象類,抽象出幾個行為狀態(tài)
public abstract class State {
/**
* 投幣
*/
public abstract void insertQuarter();
/**
* 退幣
*/
public abstract void ejectQuarter();
/**
* 轉(zhuǎn)動出糖曲軸
*/
public abstract void turnCrank();
/**
* 發(fā)糖
*/
public abstract void dispense();
/**
* 退還硬幣
*/
protected void returnQuarter() {
System.out.println("退幣……");
}
}
// 2、為每個狀態(tài)實現(xiàn)接口或基類
/**
* 沒有硬幣的狀態(tài)
*/
public class NoQuarterState extends State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("你投入了一個硬幣");
//轉(zhuǎn)換為有硬幣狀態(tài)
gumballMachine.setState(gumballMachine.hasQuarterState);
}
@Override
public void ejectQuarter() {
System.out.println("沒有硬幣,無法彈出");
}
@Override
public void turnCrank() {
System.out.println("請先投幣");
}
@Override
public void dispense() {
System.out.println("沒有投幣,無法發(fā)放糖果");
}
}
/**
* 投硬幣的狀態(tài)
*/
public class HasQuarterState extends State{
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("請不要重復(fù)投幣!");
returnQuarter();
}
@Override
public void ejectQuarter() {
returnQuarter();
gumballMachine.setState(gumballMachine.noQuarterState);
}
@Override
public void turnCrank() {
System.out.println("轉(zhuǎn)動曲軸,準(zhǔn)備發(fā)糖");
gumballMachine.setState(gumballMachine.soldState);
}
@Override
public void dispense() {
System.out.println("this method don't support");
}
}
/**
* 出售的狀態(tài)
*/
public class SoldState extends State{
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("已投幣,請等待糖果");
returnQuarter();
}
@Override
public void ejectQuarter() {
System.out.println("無法退幣,正在發(fā)放糖果,請等待");
}
@Override
public void turnCrank() {
System.out.println("已按過曲軸,請等待");
}
@Override
public void dispense() {
int candyCount = gumballMachine.getCandyCount();
if(candyCount > 0){
System.out.println("分發(fā)一顆糖果");
candyCount--;
gumballMachine.setCandyCount(candyCount);
if(candyCount > 0){
gumballMachine.setState(gumballMachine.noQuarterState);
return;
}
}
System.out.println("抱歉,糖果已售盡");
gumballMachine.setState(gumballMachine.soldOutState);
}
}
/**
* 售盡的狀態(tài)
*/
public class SoldOutState extends State{
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("糖果已經(jīng)售盡");
returnQuarter();
}
@Override
public void ejectQuarter() {
System.out.println("沒有投幣,無法退幣");
}
@Override
public void turnCrank() {
System.out.println("糖果已經(jīng)售盡");
}
@Override
public void dispense() {
System.out.println("糖果已經(jīng)售盡");
}
}
//3、將糖果機(jī)的動作委托到狀態(tài)類
public class GumballMachine extends State{
public State noQuarterState = new NoQuarterState(this);
public State hasQuarterState = new HasQuarterState(this);
public State soldState = new SoldState(this);
public State soldOutState = new SoldOutState(this);
private State state = soldOutState;
private int candyCount = 0;
public GumballMachine(int count) {
this.candyCount = count;
if(count > 0)
setState(noQuarterState);
}
@Override
public void insertQuarter() {
state.insertQuarter();
}
@Override
public void ejectQuarter() {
state.ejectQuarter();
}
@Override
public void turnCrank() {
state.turnCrank();
}
@Override
public void dispense() {
state.dispense();
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setCandyCount(int candyCount) {
this.candyCount = candyCount;
}
public int getCandyCount() {
return candyCount;
}
}
從代碼里面可以看出,糖果機(jī)根據(jù)此刻不同的狀態(tài),而使對應(yīng)的動作呈現(xiàn)不同的結(jié)果。這份代碼已經(jīng)可以滿足我們的基本需求,但稍微思考一下,你會覺得這種實現(xiàn)代碼似乎,功能太復(fù)雜了,擴(kuò)展性很差,沒有面向?qū)ο蟮娘L(fēng)格。
假設(shè)由于新需求,要增加一種狀態(tài),那每個動作方法我們都需要修改,都要重新增加一條else語句。而如果需求變更,某個狀態(tài)下的動作需要修改,我們也要同時改動四個方法。這樣的工作將是繁瑣而頭大的。
可以發(fā)現(xiàn),這種模式下,糖果機(jī)根本不需要清楚狀態(tài)的改變,它只用調(diào)用狀態(tài)的方法就行。狀態(tài)的改變是在狀態(tài)內(nèi)部發(fā)生的。這就是"狀態(tài)模式"。
如果此時再增加一種狀態(tài),糖果機(jī)不需要做任何改變,我們只需要再增加一個狀態(tài)類,然后在相關(guān)的狀態(tài)類方法里面增加轉(zhuǎn)換的過程即可
-
除常用方法(如 getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復(fù)雜的語句,將復(fù)雜邏輯判斷的結(jié)果賦值給一個有意義的布爾變量名,以提高可讀性。
說明:很多 if 語句內(nèi)的邏輯相當(dāng)復(fù)雜,閱讀者需要分析條件表達(dá)式的最終結(jié)果,才能明確什么樣的條件執(zhí)行什么樣的語句,那么,如果閱讀者分析邏輯表達(dá)式錯誤呢?
正例:
// 偽代碼如下
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
(八)注釋規(guī)范
-
類、類屬性、類方法的注釋必須使用 Javadoc 規(guī)范,使用/**內(nèi)容*/格式,不得使用// xxx 方式
-
所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實現(xiàn)什么功能。
-
方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/ /注釋,注意與代碼對齊。
-
所有的枚舉類型字段必須要有注釋,說明每個數(shù)據(jù)項的用途。
-
代碼修改的同時,注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù)、返回值、異常、核心邏輯等的修改。代碼和注釋要同步更新
-
對于注釋的要求:第一、能夠準(zhǔn)確反應(yīng)設(shè)計思想和代碼邏輯;第二、能夠描述業(yè)務(wù)含義,使別的程序員能夠迅速了解到代碼背后的信息。注釋要盡可能精簡準(zhǔn)確、表達(dá)到位,要避免過多過濫的注釋。
-
特殊注釋標(biāo)記,請注明標(biāo)記人與標(biāo)記時間。注意及時處理這些標(biāo)記,通過標(biāo)記掃描,經(jīng)常清理此類標(biāo)記。線上故障有時候就是來源于這些標(biāo)記處的代碼。
-
待辦事宜(TODO):( 標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])表示需要實現(xiàn),但目前還未實現(xiàn)的功能。這實際上是一個 Javadoc 的標(biāo)簽,目前的 Javadoc還沒有實現(xiàn),但已經(jīng)被廣泛使用。只能應(yīng)用于類,接口和方法(因為它是一個 Javadoc 標(biāo)簽)。
-
錯誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])在注釋中用 FIXME 標(biāo)記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。
(九)其他
-
獲取當(dāng)前毫秒數(shù) System.currentTimeMillis(); 而不是 new Date().getTime();說明:如果想獲取更加精確的納秒級時間值,使用 System.nanoTime()的方式。在 JDK8 中,針對統(tǒng)計時間等場景,推薦使用 Instant 類
-
不要在controller層加任何復(fù)雜的邏輯
-
任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造或初始化,都應(yīng)指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長吃光內(nèi)存
-
一個方法體不要過長,要對邏輯進(jìn)行拆分,盡量不要超過80行