Android Primer——使用通知和手機多媒體功能
阿新 • • 發佈:2019-01-06
好的程式碼規範有助於減少軟體實現的複雜度,降低溝通成本,本規範主要涵蓋了軟體設計(UML、基本架構)、工程結構、程式設計、異常日誌、單元測試、安全等方面的規範。
1.軟體設計規範
- | 場景 | Level | 備註 |
---|---|---|---|
文件 | 需要沉澱文件的:儲存方案、底層資料結構 | 強制 | |
用例圖 | 需求分析階段,如果與系統互動的 User 超過 1 類,並且相關 User Case 超過 5 個,那麼使用用例圖來表達結構化需求。 | 強制 | |
狀態圖 | 某個業務物件的狀態超過 3 個,那麼應該使用狀態圖來表達並且明確狀態變化的各個觸發條件。 | 強制 | |
時序圖 | 某個功能的呼叫鏈路上涉及的物件超過 3 個,則應使用時序圖來表達並且明確各呼叫環節的輸入與輸出。 | 強制 | |
類圖 | 如果系統中模型類超過 5 個,並且存在複雜的依賴關係,則應使用類圖來表達並且明確類之間的關係。 | 強制 | |
活動圖 | 如果系統中超過 2 個物件之間存在協作關係,並且需要表示複雜的處理流程,則使用活動圖來表示。 | 強制 | |
敏捷開發 | 敏捷開發是快速交付迭代可用的系統,省略多餘的設計方案,摒棄傳統的審批流程,但需要核心關鍵點上的必要設計和文件沉澱。 | 參考 |
2.工程結構規範
1.應用分層
層級依賴關係 如下(預設上層依賴於上層,箭頭關係表示可直接依賴):
- | 說明 |
---|---|
終端顯示層 | 各個端的模板渲染並執行顯示的層,當前主要包括:Freemarker、Thymeleaf、Velocity、JS、JSP、移動端展示等。 |
開發介面層 | 目前主要包括:可直接封裝 Service 方法暴露成 RPC 介面;通過 Web 封裝為 HTTP 介面;進行閘道器安全控制、流量控制等。 |
Web層 | 主要是對訪問控制進行轉發,對各類基本引數校驗,或者對不復用的業務簡單處理等。 |
Service層 | 具體業務邏輯服務層。 |
Manager層 | 通用業務處理層。對第三方平臺封裝的層,預處理返回結果及轉化異常資訊;對 Service 層通用能力的下沉,如快取方案、中介軟體通用處理;與 DAO 互動,對多個 DAO 的組合複用。 |
DAO層 | 資料庫訪問層,與底層 MySQL、Oracle、HBase 等進行資料互動。 |
外部介面或第三方平臺 | 包括其他部門的 RPC 開放介面、基礎平臺、其他公司的 HTTP 介面。 |
分層領域模型規範如下:
- | 說明 |
---|---|
DO | Data Object。與資料庫表結果一一對應,通過 DAO 層向上傳輸資料來源物件。 |
DTO | Data Transfer Object,資料傳輸物件。Service 或 Manager 向外傳輸的物件。 |
BO | Business Object,業務物件。由 Service 層輸出的封裝業務邏輯的物件。 |
AO | Application Object,應用物件。在 Web 層與 Service 層之間抽象的複用物件模型,極為貼近展示層,複用度不高。 |
VO | View Object,顯示層物件。通常是 Web 向模板渲染引擎傳輸的物件。 |
Query | 資料查詢物件。各層接受上層的查詢請求。如果是超過 2 個引數的查詢封裝,則禁止使用 Map 類來傳輸。 |
2.二方庫依賴
二方庫依賴的規範:
- | 規範 | Level | 備註 |
---|---|---|---|
GroupID | 格式: com.{公司/BU}.業務線.[子業務線],最多五級,子業務線可選。 | 強制 | |
ArtifactID | 格式: 產品線名-模組名。語義不重複不遺漏。 | 強制 | |
Version | 格式: 主版本號.次版本號.修訂號。主版本號,產品方向改變,或大規模 API 不相容,或架構不相容升級;次版本號,保持相對相容性,增加主要功能特性,影響範圍極小的 API 不相容修改;修訂號,保持完全相容性,修復 bug、新增次要功能特性等。起始版本號必須為 1.0.0。 | 強制 | |
SNAPSHOT 版本 | 線上應用不要依賴 SNAPSHOT 版本 (安全包除外),不依賴 SNAPSHOT 版本是保證應用釋出的冪等性。另外,也可以加快編譯時的打包構建。 | 強制 | |
二方庫的新增、升級 | 需要保證除功能之外的其他 jar 包仲裁結果不變。如果有改變,需要明確評估與驗證,建議進行 dependency:resolve 前後資訊對比,如果仲裁結果完全不一致,則通過 dependency:tree 命令找出差異點,進行< excludes> 排除 jar 包。 | 強制 | |
二方庫中列舉 | 二方庫中允許定義列舉,引數可以使用列舉型別,但是介面返回值不允許使用列舉型別或者包含列舉型別的 POJO 物件。 | 強制 | |
二方庫群 | 依賴於一個二方庫群時,必須定義一個統一的版本變數 (例如 ${spring.version},定義 Spring 依賴的時候引用該版本),避免版本號不一致。 | 強制 | |
子專案pom | 禁止子專案的pom依賴中出現相同的 GroupID、ArtifactID,不同的 Version。 | 強制 |
二方庫釋出者應當遵守以下規範:
規範 | Level | 備註 |
---|---|---|
精簡可控制,移除一切不必要的 API 和依賴,只包含 Service API、必要的領域模型物件、Utils 類、常量、列舉等。如果依賴其他二方庫,儘量是 provided 引入,讓二方庫使用者依賴具體版本號;無 log 具體實現,只依賴日誌框架。 | 參考 | |
穩定可追溯原則,每個版本的變化都應該被記錄,二方庫由誰維護,原始碼在哪裡,都需要能方便地查到。除非使用者主動升級版本,否則公共二方庫的行為不應該發生變化。 | 參考 |
3.高併發伺服器
- | 規範 | Level | 備註 |
---|---|---|---|
高併發伺服器建議調小 TCP 協議的 time_wait 超時時間。 | 作業系統預設 240s 後,才會關閉處於 time_wait 狀態的連線。在高併發訪問下,伺服器會因為處於 time_wait 的連線數過多,而無法建立新的連線,所以需要在伺服器上調小這個值。在 Linux 伺服器上可通過變更 /etc/sysctl.conf 檔案去修改該預設值(s):net.ipv4.tcp_fin_timeout = 30 | 推薦 | |
調大伺服器所支援的最大檔案控制代碼數(fd) | 主流作業系統的設計是將 TCP/UDP 連線採用與檔案一樣的方式管理,即一個連線對應一個 fd。Linux 伺服器預設支援最大的 fd 數量為 1024,當併發連線數很大時很容易因為 fd 不足而出現 “open too many files” 錯誤,導致新的連線無法建立。建議將 Linux 伺服器所支援的最大控制代碼數調高數倍 (與伺服器的記憶體數量相關)。 | 推薦 | |
JVM 引數 | JVM 的堆最小值 Xms 和堆最大值 Xmx 引數設定一樣大小的記憶體容量,避免在 GC 後調整堆大小帶來的壓力。 | 推薦 | |
給 JVM 設定 -XX:+HeapDumpOnOutOfMemoryError 引數,讓 JVM 遇到 OOM 場景時輸出 dump 資訊。 | 推薦 |
3.寫程式碼的規範
1.命名規範
- | 規範 | Level | 備註 |
---|---|---|---|
類名 | 使用 UpperCamelCase 風格,但 DO/BO/DTO/VO/AO/PO 等情形除外(例如 UserService、UserDO)。 | 強制 | |
方法名、引數名、成員變數、區域性變數 | 駝峰命名法。 | 強制 | |
常量命名 | 全部大寫,單詞間用下畫線隔開。 | 強制 | |
抽象類 | 使用 Abstract 或 Base 開頭 | 強制 | |
異常類 | 使用 Exception 結尾。 | 強制 | |
列舉類 | 使用 Enum 結尾。列舉成員名稱需要全大寫,單詞間用下畫線隔開。 | 強制 | |
測試類 | 使用測試的類名開始,以 Test 結尾。 | 強制 | |
定義陣列 | 型別與中括號之間無空格相連定義陣列,例如 int[] arrayDemo; | 強制 | |
Service/DAO 層方法命名 | 獲取單個物件的方法用 get 作為字首,獲取多個物件用 list;獲取統計值的方法用 count 作為字首;插入的方法用 save/insert 作為字首;刪除的方法用 remove/delete 作為字首;修改的方法用 update 作為字首。 | 參考 |
2.程式碼規範
規範 | Level | 備註 |
---|---|---|
if/for/while/switch/do 等保留字與括號之間必須加空格。 | 強制 | |
採用 4 個空格縮排,禁止使用 Tab 控制符。 | 強制 | |
註釋的雙斜線與註釋內容之間有且僅有一個空格。例如: // 這是註釋。 | 強制 | |
所有覆寫方法,必須加 @Override 註解。 | 強制 | |
介面過時,必須加 @Deprecated 註解,並清晰地說明採用的新介面或者新服務是什麼。 | 強制 | |
不能使用過時的類或方法。 | 強制 | |
POJO 類必須寫 toString 方法,便於排查問題。 | 強制 | |
對於集合,只要重寫 equals 就必須重寫 hashCode。 | 強制 | |
獲取當前毫秒數用 System.currentTimeMillis(); | 強制 | |
在迴圈體內,字串的連線方式使用 StringBuilder 的 append 方法進行擴充套件。 | 推薦 | |
任何資料結構的構造或初始化,都應指定大小,避免因資料結構無限增長而耗盡記憶體。 | 推薦 |
3.併發處理規範
規範 | Level | 備註 |
---|---|---|
獲取單例物件時需要保證執行緒安全,其中的方法也要保證執行緒安全。 | 強制 | |
在建立執行緒或執行緒池時,請指定有意義的執行緒名稱,方便出錯時回溯。 | 強制 | |
執行緒資源必須通過執行緒池提供,不允許在應用中自己顯式建立執行緒。 | 強制 | |
執行緒池不允許使用 Executors 建立,而是通過 ThreadPoolExecutor 的方式建立,這樣的處理方式能讓編寫程式碼的工程師更加明確執行緒池的執行規則,避免資源耗盡的風險。 | 強制 | |
SimpleDateFormat 是執行緒不安全的類,一般不要定義為 static 變數,如果定義為 static,必須加鎖,或者使用 DateUtils 工具類。 | 強制 | |
在高併發場景中,加鎖的程式碼塊工作量儘可能小,避免在鎖程式碼塊中呼叫 RPC 方法。在對多個資源、資料庫表、物件同時加鎖時,需要保持一致的加鎖順序,避免死鎖。 | 強制 | |
在併發修改同一記錄時,為避免更新丟失,需要加鎖。要麼在應用層加鎖,要麼在快取層加鎖,要麼在資料庫層使用樂觀鎖(使用 version 作為更新依據,每次訪問衝突概率小於 20% 推薦樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數不得小於 3 次)。 | 強制 | |
避免 Random 例項被多執行緒使用,雖然共享該例項是執行緒安全的,但是因競爭同一 seed 導致效能下降。在 JDK1.7 之後可以直接使用 ThreadLocalRandom,而在 JDK1.7 之前,需要編碼保證每個執行緒持有一個例項。 | 推薦 | |
在併發場景下,通過雙重檢查鎖(double-checked-locking)實現延遲初始化的優化問題隱患,推薦解決方案是較為簡單的一種,即目標屬性宣告為 volatile 型。 | 推薦 | |
volatile 解決了多執行緒記憶體不可見問題。對於一寫多讀,可以解決變數同步問題。但是,如果多寫,同樣無法解決執行緒安全問題。如果是 count++ 操作,推薦使用如下類實現: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK1.8,推薦使用 LongAdder 物件,它比 AtomicLong 效能更好(減少了樂觀鎖的重試次數)。 | 參考 | |
HashMap 在容量不夠時會進行 resize 操作,由於高併發可能出現死鏈,導致 CPU 佔用飆升,在開發過程中可以使用其他資料結構或加鎖來避免此風險。 | 參考 | |
ThreadLocal 無法解決共享物件的更新問題,ThreadLocal 物件建議使用 static 修飾。這個變數是針對一個執行緒內所有操作共享的。 | 參考 |
4.異常日誌規範
1.異常處理
- | 規範 | Level | 備註 |
---|---|---|---|
DAO 層 | 分層異常處理,在 DAO 層產生的異常型別有很多,無法細粒度進行 catch,應該使用 catch 方式,並 throw new DAOException(e),不需要列印日誌。 | 參考 | |
Service 層 | 日誌在 Service 層,必須要捕獲並寫到日誌檔案中去,記錄到磁碟,儘可能帶上引數資訊,保護案發現場。 | 參考 | |
Web 層 | Web 層絕不應該繼續往上拋異常。如果該異常將導致頁面無法正常渲染,可直接跳轉到錯誤頁面;開放介面層需要將異常處理成錯誤碼和錯誤資訊方式返回。 | 參考 |
2.日誌規範
規範 | Level | 備註 |
---|---|---|
應用中不可直接使用日誌框架 (Log4j、Logback) 中的 API,而應依賴使用日誌框架 SLF4J 中的 API。使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一。 | 強制 | |
日誌檔案推薦儲存 15 天。 | 強制 | |
應用中的擴充套件日誌 (如打點、臨時監控、訪問日誌等) 命名方式: appName_logType_logName.log,logType 為日誌型別,推薦分類有 stats/monitor/visit 等;logName 為日誌描述。 | 強制 | |
避免重複列印日誌,否則浪費磁碟空間。在日誌配置檔案中設定 additivity=”false”。 | 強制 | |
異常資訊應該包括兩類: 案發現場資訊和異常堆疊資訊。如果不處理,那麼通過 throws 往上丟擲。例如: logger.error(各類引數或者物件toString + “_” + e.getMessage(), e); | 強制 | |
生產環境禁止輸出 debug 日誌;有選擇的輸出 info 日誌。 | 推薦 |
5.安全規範
規範 | Level | 備註 |
---|---|---|
隸屬於使用者個人的頁面或者功能,必須進行許可權控制校驗。 | 強制 | |
敏感資料禁止直接展示 | 強制 | |
使用者請求傳入的任何引數必須做有效性校驗。 | 強制 | |
禁止向 HTML 頁面輸出未經安全過濾或未正確轉義的使用者資料。 | 強制 | |
表單、Ajax 提交必須執行 CSRF 安全過濾。 | 強制 | |
在使用平臺資源時,如簡訊、郵件、電話、下單、支付等,必須實現正確的防重放限制,如數量限制、疲勞度控制、驗證碼校驗,避免被濫刷、資損。 | 強制 | |
針對發帖、評論、傳送即時訊息等使用者生成內容的場景,必須實現防刷、文字內容違禁詞過濾等風控策略。 | 推薦 |