62
Coding Guideline 報報報Nelson Chuang DC

Coding guideline

  • Upload
    -

  • View
    671

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Coding guideline

Coding Guideline

報告人: Nelson Chuang

部 門: DC

Page 2: Coding guideline

2

簡報大綱

重要原則 寫碼習慣 較佳的編碼方式 效能考量

Page 3: Coding guideline

3

簡報大綱

重要原則 寫碼習慣 較佳的編碼方式 效能考量

Page 4: Coding guideline

4

什麼是好的程式碼 ?

語法 (syntax) 正確 達到功能要求 經得起考驗 (strong code) 注意效能 ( 執行速度 )

容易閱讀及理解 直觀的邏輯 自然的用語 慣用的語言用法 有意義的名稱 工整的格式 有用的註解

Page 5: Coding guideline

5

重要原則

1. 遵守命名原則2. 強制變數宣告3. 程式結構模組化4. 適當錯誤 ( 例外 ) 處理 5. 必要的註解

Page 6: Coding guideline

6

重要原則 寫碼習慣 較佳的編碼方式 效能考量

Page 7: Coding guideline

7

寫碼習慣

遵守命名原則 使用在 變數、函式 、物件 及檔案等 應該具備有意義、簡明、易記的特性 以程式的內容、目的 及範圍來定義

變數範圍及型態使用簡短名稱 函數使用主動式名稱 儘可能使用有意義的英文縮寫表示

Ex: <範圍 >+<型態 >+<用途 >

gstrGetUsrID 匈牙利命名法 (Hungarian Notation) !!!???

原始用意為看到變數就能知道其類型與用途

Page 8: Coding guideline

8

寫碼習慣

匈牙利命名法 (Hungarian Notation) !!!???

原始用意 : 看到變數就能知道其類型與用途

Page 9: Coding guideline

9

寫碼習慣 變數命名規則

開頭第一個字元使用字母或底線 不要使用空格或符號 不要濫用缩寫,除非缩寫形式為大家所公認。 不要定義依赖字母的大小寫才能區别的名稱。

例如 myValue 和 myvalue 。 不要把易混淆的數字和字母放在一起。例如

bool b001 = (lo == l0) ? (I1 == 11) : (lOl != 101); 避免使用保留字 , 例如 : integer

範例 : CustomerName (Pascal 命名法 ) accountBalance (camel 命名法 )

Page 10: Coding guideline

10

寫碼習慣

變數使用 強迫宣告變數 儘量避免使用全域變數 變數宣告後,應設定其初始值 將程式會使用到的固定的值定義成常數 注意轉型

Page 11: Coding guideline

11

寫碼習慣 函數與程序 (Funciotn & Sub

Procedure) 使用回傳值的函數時,不需加前置字元,

如 ex: strTemp = gstrGetFunId() 呼叫程序時,需使用  Call,

如 ex: Call msgbox(“ 哈囉 !”) 參數傳遞時,若不為 By Referance 則必須加上

By Val(VB.net 預設為 By Value); 若無參數傳遞時,也須加上 ();

註: Java Script 的函數都為回傳值的funciton ,且不能使用 call ,而若該函數沒有回傳值,則須要設定  return

Page 12: Coding guideline

12

寫碼習慣 程式可讀性:

1. 適當的加入空白作為分隔;2. 利用縮排來顯示結構 ( 若程式碼右邊的一個畫面看不完時就表

示巢狀太多層了 ) ;3. 將過長 ( 超出螢幕寛度 ) 的程式碼、 SQL Statement、字串做有意義的分段 ( 包含程式中串 SQL)

4. 一個程序最好不要寫超過一個畫面的大小,以易於管理;5. 加上括弧讓意義明確6. 以最自然的形式使用運算式7. 拆解複雜的表示式8. 採用 else-if 進行多重決策9. 使用字元常數而不要使用整數10. 為魔術數字取名字

Page 13: Coding guideline

13

寫碼習慣 程式註解:

1. 程式中於每一段處理程序前及不能一眼看懂的均要加註說明2. 要清楚且簡潔不重覆 ( 重質不重量 ) ;3. 註解之句子必須清楚易懂,不要讓閱讀者曲解;4. 學習寫不需要註解的程式碼以降低為程式加註解的需求;5. 不要與程式碼抵觸6. 不要把工夫花在很清楚的程式碼上7. 不要對爛程式碼註解 ( 乾脆改寫它 )

8. 必須隨程式碼更新而更新9. 為函數及全域性資料加上註解10. 讓程式碼更清楚,不要產生困惑

Page 14: Coding guideline

14

寫碼習慣 一般程式 / 函數表頭:

‘*******************************************************'* 程式 / 函數代號:'* 程式 / 函數名稱:'* 目 的: '* 參數說明:'* 傳回值 :'* 副作用 :'* 備 註 :'* 範 例 :'* 版本變更:'* xx. YYYY/MM/DD VER AUTHOR COMMENTS

'* === ========== ===== ======= ==========

'' 1. 2003/01/01 Nelson_Chuang New Create

‘*******************************************************

Page 15: Coding guideline

15

寫碼習慣 錯誤 ( 例外 ) 處理:

程式均應加入錯誤處理的程式碼; 所有應用程式可能發生的錯誤都應納入錯誤處理的範圍; 有效的處理可能發生的錯誤可以避免應用系統當掉及可

知道發生錯誤的位置; Ex: 除法運算中 , 分母為 0

在有關對資料庫做動作(Insert 、 Delete、 Update..等 ) 的程式中,當發生錯誤時,為確保資料的一致性,應適時將資料做RollBack. 以 ASP為例 , 在程式的最上方加入 Transaction 控制 .

Page 16: Coding guideline

16

寫碼習慣 其它:

在撰寫時,應有細密的心思,並且多考慮各種情況及是否產生副作用

撰寫風格前後儘量一致 使用慣用方法來保持一致性 在程式撰寫時,若遇到無法找出的問題,請立即尋求協助,幫忙排除解決 (Debug)。

使用測試資料時儘可能在明顯地方註記,最後測試完成請刪除測試碼 , 應避免將測試資料寫死在程式中。

EX:測試資料請在, ''------ 測試資料,用完請刪除  -start ''----- end ..並儘量使用 if xxx='' then xxx='001' 等語法代替.

刪除不必要的程式碼。 完成某一功能函數時 , 就要做編譯及測試 要有責任心

Page 17: Coding guideline

17

簡報大綱

重要原則 寫碼習慣 較佳的編碼方式 效能考量

Page 18: Coding guideline

18

較佳的編碼方式

寫 code 不難 先求有再求好 可否第一次就寫好 ? 重構 & 預構

Page 19: Coding guideline

19

何謂重構

Refactoring 改變程式的寫法,但是不影響程式的執行結果

利用測試確保改變程式的寫法,並未改變程式執行結果

使程式執行更有效率,更容易閱讀與維護,或更不容易出錯

Page 20: Coding guideline

20

何謂預構

Prefactoring (ken Pugh)

利用你從經驗累積而得的洞查力以及他人經驗,藉以在程式開發時,減少重構的工作量

Page 21: Coding guideline

21

將程式碼段落製成新的方法

Sub printOwing() printBanner() ‘print details Console.WriteLine(“name:”&_name) Console.WriteLine(“amount”& getOutstanding())End Sub

Refactoring前

Sub printOwing() printBanner() printDetails(getOutstanding())End Sub

Sub printDetails (ByVal outstanding As double) Console.WriteLine(“name:”&_name) Console.WriteLine(“amount” & outstanding)End Sub

Refactoring後

Refactor

Page 22: Coding guideline

22

使用 Guard Clause 取代巢狀判斷

Function getPayAmount() As Double Dim result As Double lf_isDead Then result=deadAmount() Else lf_isSeparated Then result=separatedAmount() Else lf_isRetired Then result=retiredAmount() Else result=normalPayAmount() End lf End lf End lf Return resultEnd Function

Refactoring前

Function getPayAmount() As Double lf_isDead Then Return deadAmount() End lf lf_isSeparated Then Return separatedAmount() End lf lf_isRetired Then Return retiredAmount() End lf Return normalPayAmount()End Function

Refactoring後

Refactor

Page 23: Coding guideline

23

Function disabilityAmount() As Double lf_seniority < 2 Then Return 0 End lf lf_monthsDisabled > 12 Then Return 0 End lf lf_isPartTime Then Return 0 End lf ‘compute the disability amountEnd Function

Refactoring前

Consolidate 判斷式 - 將數個判斷條件整理成函數

Function disabilityAmount() As Double

lf isNotEligableForDisability() Then

Return 0

End lf

‘ compute the disability amount

End Function

Refactoring後

Refactor

Page 24: Coding guideline

24

抽出重覆的程式碼

lf is SpecialDeal() Then total = price * 0.95 send()Else total = price * 0.98 send()End lf

Refactoring前

lf is SpecialDeal() total = price * 0.95Else total = pirce * 0.98End lfSend()

Refactoring後

Refactor

Page 25: Coding guideline

25

整合邏輯運算式

lf Date.before(SUMMER_START) Or Date.after(SUMMER_END) Then charge = quantity * _winterRate + _winterServiceChargeElse charge = quantity * _summerRateEnd lf

Refactoring前

lf notSummer(date) Then charge = winterCharge(quantity)Else charge = summerCharge(quantity)End lf

Refactoring後

Refactor

Page 26: Coding guideline

26

做好轉型

Function lastReading() As Object Return readings.lastElement()End Function

Refactoring前

Function lastReading() As Reading Return CType(readings.lastElement(), Reading)End Function

Refactoring後

Refactor

Page 27: Coding guideline

27

Refactor 封裝類別中的變數

Class Class 1 Public _name As String

End Class

Refactoring前

Class Class 1 Private _name As String Public Function getName() As String Return _name End Function

Public Sub setName(ByVal arg As String) _name = arg End SubEnd Class

Refactoring後

Page 28: Coding guideline

28

Refactor 去除不必要的函數

Function getRating() As lnteger Return llf(more ThanFiveLateDeliveries(), 2, 1)End Function

Function more ThanFiveLateDeliveries() As Boolean Return_numberOfLateDeliveries > 5End Function

Refactoring前

Function getRating() As lnteger Return llf(_numberOfLateDeliveries > 5, 2, 1)End Function

Refactoring後

Page 29: Coding guideline

29

去除不必要的變數

Dim basePrice As Double = anOrder.basePrice()Return (basePrice > 1000)

Refactoring前

Return anOrder.basePrice() > 1000

Refactoring後

Refactor

Page 30: Coding guideline

30

使用有意義的變數名稱簡化運算式  

If platform.ToUpper().lndexOf(“MAC”) > -1 And _ browser.ToUpper().lndexOf(“IE”) > -1 And _ wasInitialized() And resize > 0 Then ‘ do somethingEnd If

Refactoring前

Dim isMacOs As Boolean = platform.ToUpper().IndexOf(“MAC”) > -1Dim is IE Browser As Boolean = browser.ToUpper().IndexOf(“IE”) > -1Dim was Resized As Boolean = Resize > 0If isMacOs && isIEBrowser && wasInitialized() && wasResized Then ‘ do somethingEnd If

Refactoring後

Refactor

Page 31: Coding guideline

31

使用 Assert檢查假設的條件  

Function getExpenseLimit() As Double ‘should have either expense limit or a primary project return IIF(_expenseLimit !=NULL_EXPENSE,_expenseLimit,_ _primaryProject.getMemberExpenseLimit())End Function

Refactoring前

Function getExpenseLimit() As Double Debug.Assert(_expenseLimit != NULL_EXPENSE Or _primaryProject != null) return IIF(_expenseLimit != NULL_EXPENSE, _expenseLimit, _ _primaryProject.getMemberExpenseLimit())End Function

Refactoring後

Refactor

Page 32: Coding guideline

32

使用物件當做參數 ( 一 )

Dim newStart As Date Time = New Date Time(previous End.Year(), _ previous End.Month(), previousEnd.Day() + 1)

Refactoring前

Dim newStart As DateTime = nextDay(previousEnd)

Private Shared Function nextDay(ByVal arg As DateTime) As DateTime Return New DateTime(arg.Year(), arg.Month(), arg.Day() + 1)End Function

Refactoring後

Refactor

Page 33: Coding guideline

33

使用物件當做參數 ( 二 )

Dim low As Integer = daysTempRange().getLow()Dim high As Integer = daysTempRange().getHigh()withinPlan = plan.withinRange(low, high)

Refactoring前

withinPlan = plan.withinRange(daysTempRange())

Refactoring後

Refactor

Page 34: Coding guideline

34

避免直接使用父類別宣告的變數

Public Class Manager Inherits Employee Public Sub New(ByVal name As String, ByVal id As String) _name = name _id = id End SubEnd Class

Refactoring前

Public Class Manager Inherits Employee Public Sub New(ByVal name As String, ByVal id As String) MyBase.New(name, id) End SubEnd Class

Refactoring後

Refactor

Page 35: Coding guideline

35

縮小區域變數可見範圍

Sub foo() Dim I As Integer = 7 ‘ I is not used here If someCondition Then ‘ i is used only within this block End If ‘ i is not used hereEnd Sub

Refactoring前

Sub foo() If someCondition Then Dim i as integer = 7 ‘ i is used only within this block End IfEnd Sub

Refactoring後

Refactor

Page 36: Coding guideline

36

避免指派內容值給參數

Function discount(ByVal inputVal As Integer, ByVal quantity As Integer, _ ByVal yearToDate As Integer) As Integer If inputVal > 50 Then inputVal-= 2 End IfEnd Function

Refactoring前

Function discount(ByVal inputVal As Integer, ByVal quantity As Integer, _ ByVal yearToDate As Integer) As Integer Dim result As Integer = inputVal If inputVal > 50 Then result -= 2 End IfEnd Function

Refactoring後

Refactor

Page 37: Coding guideline

37

避免負負得正的思考邏輯

If Not item.isNotFound() Then

End If

Refactoring前

If item.isFound() Then

End If

Refactoring後

Refactor

Page 38: Coding guideline

38

使用例外代表錯誤

Function withdraw(ByVal amount As Integer) As Integer If amount > _balance Then Return -1 Else _balance -= amount Return 0 End IfEnd Function

Refactoring前

Sub withdraw(ByVal amount As Integer) If amount > _balance Then Throw New BalanceException _balance -= amount End IfEnd Sub

Refactoring後

Refactor

Page 39: Coding guideline

39

使用判斷語法取代例外處理

Function getValueForPeriod(ByVal periodNumber As Integer) As Double Try Return _values(periodNumber) Catch ex As IndexOutOfRangeException Return 0 End TryEnd Function

Refactoring前

Function getValueForPeriod(ByVal periodNumber As Integer) As Double If periodNumber >= _values.Length Then Return 0 End If Return _values(periodNumber)End Function

Refactoring後

Refactor

Page 40: Coding guideline

40

使用迴圈取代遞迴

Public Sub countDown(ByVal n As Integer) If n = 0 Then Return End If waitASecond() countDown(n - 1)End Sub

Refactoring前

Public Sub countDown(ByVal n As Integer) While n > 0 waitASecond() n -= 1 End WhileEnd Sub

Refactoring後

Refactor

Page 41: Coding guideline

41

利用參數讓函數更好用

Sub printValues() Dim i As Integer For i = 0 To people.Length - 1 Console.WriteLine(people(i).name & “ has salary “ & people(i).salary) NextEnd Sub

Refactoring前

Sub printValues(ByVal outfile As PrintStream) Dim i As Integer For i = 0 To people.Length - 1 outfile.printIn(people(i).name & “ has salary “ & people(i).salary) NextEnd Sub

Refactoring後

Refactor

Page 42: Coding guideline

42

簡報大綱

重要原則 寫碼習慣 較佳的編碼方式 效能考量

Page 43: Coding guideline

43

效能 (1)

調整程式碼 展開迴圏或是將他們刪除 以快取緩衝區處理經常使用的值 集中共同的子表示式 分開處理特殊案例 預先計算結果 利用近似值 以低階語言改寫

Page 44: Coding guideline

44

效能 (2)

採用較佳的演算法或資料結構 啓動編譯程式最佳功能 將注意力集中在問題點上 無關緊要的東西不要做最佳化處理 採用可能的最小資料型態來節省空間 不要儲存你能輕易計算的東西 運用效能分析工具 ; 使時間測定自動化

Page 45: Coding guideline

45

ASP 效能

1. 將經常使用的資料快取處理在 Web伺服器上2. 將經常使用的資料快取在應用程式或工作階段物件中3. 將資料和 HTML 快取在 Web伺服器的磁碟上4. 避免將非敏捷元件快取在應用程式或工作階段物件中5. 不要將資料庫連線快取在應用程式或工作階段物件中6. 合理使用工作階段物件7. 將程式碼封裝在 COM 物件中8. 遲一點取得資源,早一點釋放資源

Page 46: Coding guideline

46

ASP 效能

9. 跨程序執行用效能交換可靠性10. 使用 Option Explicit

11. 在副常式和函數中使用區域變數12. 將經常使用的資料複製到 Script 變數中13. 避免重新確定陣列的維數14. 使用回應緩衝15. 批次處理內嵌 Script 和 Response.Write 陳述式16. 在開始長途旅行之前使用

Response.IsClientConnected

Page 47: Coding guideline

47

ASP 效能

17. 使用 <OBJECT> 列舉物件18. 對於 ADO 和其它元件使用 TypeLib 連結19. 利用瀏覽器的驗證功能20. 避免在迴圈中使用字串並列21. 啟用瀏覽器和 Proxy 快取處理22. 盡可能使用 Server.Transfer 代替

Response.Redirect

23. 在目錄 URL 中使用反斜線

Page 48: Coding guideline

48

ASP.NET 效能

1. 工作階段 (Session) 狀態沒有使用時,請將其停用2. 小心選擇工作階段狀態提供者3. 避免大量對伺服器來回存取4. 使用 Page.IsPostback 以避免來回存取的額外工作5. 謹慎並正確地使用伺服器控制項6. 避免大量的伺服器控制項檢視狀態7. 使用 Response.Write 串連字串8. 不要依賴程式碼中的例外狀況 (Exception)

Page 49: Coding guideline

49

ASP.NET 效能

9. 在 Visual Basic 或 JScript 程式碼中使用早期繫結 (Early-Binding)

10. 將常被呼叫的 COM 元件移植到 Managed 程式碼11. 使用 SQL 預存程序 (Stored Procedure) 來存取資料

12. 使用 SqlDataReader 來提供快速順向的唯讀資料指標 (Cursor)

13. 儘可能快取資料和輸出14. 啟用多個處理器電腦的 Web Gardening 功能15. 不要忘記停用偵錯模式

Page 50: Coding guideline

50

效能計數器重點觀察指標計數器 說 明

# of ExceptionsThrown

引發例外的總次數

效能物件.NET CLR Exceptions

# Gen xCollections

Gen x物件被 Garbage Collector回收的次數.NET CLR Memory

% Time In GCASP.NET程式花在回收物件

的時間比例.NET CLR Memory

Promoted Memoryfrom Gen X

Gen X物件沒有被回收,而升級成為Gen Y物件所佔用的記憶體量.NET CLR Memory

Available MBytes 可用的記憶體數 ( 和 Memory Leak有關 )Memory

% Processor Time 程式執行花掉的 CPU時間比例Process

% Processor Time CPU花在執行所有程式的時間比例Processor

User Connections 資料庫的使用者連線數SQL Server :General Statistics

Page 51: Coding guideline

51

SQL 效能

精明的使用游標 (cursor) 儘可能的使用預儲程序 (stored procedure) 了解同時性 (concurrency) 與一致性

(consistency) 的互抵 分析並解決銷定的問題 分析並解決死結的問題

Page 52: Coding guideline

52

SQL 效能

批次和效能 儘量減少批次的數量 減少網路來回的次數 減少編譯的時間 可以非同步下批次命令 當同步執行時,使用者端執行並等待資料或訊息 非同步執行時,使用者端繼續執行

Page 53: Coding guideline

53

SQL 效能

各種使用者端 API 和效能 使用者端 API 提供函數呼叫或是物件介面,透過

TDS 下命令給 SQL Server 並接收資料 函數呼叫介面: DB-Library 和 ODBC 物件介面: .NET SQLClient , OLE

DB , ADO , RDO , DAO 和 DMO 依照以下的原因選擇使用者端 API 使用 API 的能力效能 最佳速度 :SQLClient , ODBC 或 OLE DB 最大的程式生產力: SQLClient , ADO 或 RDO

Page 54: Coding guideline

54

SQL 效能

Ad-Hoc SQL,Prepare-Execute 和預存程序 透過參數傳遞 ( 使用者端的 API 會決定是否 Ad-

Hoc SQL 是單純的語法還是包含參數符號 ) 以 OLE DB, ODBC 和 ADO 都支援以 ODBC 的

格式呼叫預存程序 Prepare-Execute(準備一次執行多次最好 ) Sp_Executesql 較 Prepare & Execute 或

Execute(‘string’) 有效率 Sp_Executesql 搭配參數會有更佳的效能

Page 55: Coding guideline

55

SQL 效能

預設的結果集 預設的結果集是消防水管式 (Firehose)游標 唯讀,只能向前,每次讀取一筆記錄 傳回的資料放在固定的資料串流中,除非使用者放棄

(cancel) 或用完所有的記錄,否則不會停止 在同一條連線上,不可以同時執行兩個批次,除非預設結果集已經處理完畢

可能會產生另外一條連線 預設的結果集傳資料回使用者端最有效率

Page 56: Coding guideline

56

SQL 效能 伺服器端游標

不管是由 SQL 或 API 維護的伺服器端游標,對 SQL Server 引擎的進入點都相同

Fast forward only cursors 允許在相同的連線上執行多句語法, 預設結果集則不允許

靜態 (Static)游標會建立存放完整結果集的暫存資料表 游標開啟後,裡面的資料成員固定不變 Keyset 游標在暫存資料表內存放的是主鍵 游標開啟後,裡面有哪些資料成員固定不變 Dynamic 游標是在基礎資料表上移動 資料成員是不固定的

Page 57: Coding guideline

57

SQL 效能

游標與鎖定 一般游標是 select 語法所造成的鎖定,與以下有關

交易隔離層級 From 子句所設定的鎖定提示 不同的游標型態會造成不同的鎖定 某筆記錄從讀取到修改,可能會持續被鎖定 記錄處理應該要快速 不要處理了一大堆記錄然後放棄

Page 58: Coding guideline

58

SQL 效能 查詢與搜尋限制

確定所有的過濾條件都符合 search arguments 避免在 search arguments 使用變數 避免 IN, OR NOT ,<> 減少使用暫存資料表 (temp tabes) 小心使用彙總函數 (aggregate function ) 儘量避免資料形別轉換 避免排序 選寫好的搜尋參數 在查詢中定義 where 子句 確認 OR運算子所用到的欄位都有可用的索引

Page 59: Coding guideline

59

SQL 效能

有效使用連線 應用程式在執行不同功能時 , 儘量共用連線 ,並將工

作透過批次完成 用完就斷線 , 明確的關閉連線 交易不可過長 , 若採用 pessimistic鎖定 , 可能會將

執行權交還前端 ,但交易一直存在 採用斷線資料存取

Page 60: Coding guideline

60

SQL 效能

其它 可善用 SET NOCOUNT ON 語法增加效能 避免用萬用字元開頭 (select 資料時 , 除非真的需要全部欄位資料 才下 select * from …,否則儘可能下明確的欄位資料 )

Page 61: Coding guideline

61

相關資源

http://crd.gss.com.tw/portal The Pragmatic Programmer: From Journeyman to Master, by Andrew

Hunt and David Thomas, is at Amazon.com at:http://www.amazon.com/exec/obidos/ASIN/020161622X/

A refactoring portal maintained by Martin Fowler contains links to refactoring tools and other refactoring sites:http://www.refactoring.com/

Java Coding Convention

Microsoft 官方版命名方針 Code compelete 2nd –GSS Wiki

Page 62: Coding guideline

62