Coding guideline

Preview:

Citation preview

Coding Guideline

報告人: Nelson Chuang

部 門: DC

2

簡報大綱

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

3

簡報大綱

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

4

什麼是好的程式碼 ?

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

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

5

重要原則

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

6

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

7

寫碼習慣

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

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

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

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

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

8

寫碼習慣

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

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

9

寫碼習慣 變數命名規則

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

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

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

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

10

寫碼習慣

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

11

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

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

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

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

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

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

12

寫碼習慣 程式可讀性:

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

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

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

13

寫碼習慣 程式註解:

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

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

14

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

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

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

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

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

15

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

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

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

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

16

寫碼習慣 其它:

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

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

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

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

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

17

簡報大綱

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

18

較佳的編碼方式

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

19

何謂重構

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

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

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

20

何謂預構

Prefactoring (ken Pugh)

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

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

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

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

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

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

26

做好轉型

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

Refactoring前

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

Refactoring後

Refactor

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後

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後

29

去除不必要的變數

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

Refactoring前

Return anOrder.basePrice() > 1000

Refactoring後

Refactor

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

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

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

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

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

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

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

37

避免負負得正的思考邏輯

If Not item.isNotFound() Then

End If

Refactoring前

If item.isFound() Then

End If

Refactoring後

Refactor

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

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

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

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

42

簡報大綱

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

43

效能 (1)

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

44

效能 (2)

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

45

ASP 效能

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

46

ASP 效能

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

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

Response.IsClientConnected

47

ASP 效能

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

Response.Redirect

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

48

ASP.NET 效能

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

49

ASP.NET 效能

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

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

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

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

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

51

SQL 效能

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

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

52

SQL 效能

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

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

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 搭配參數會有更佳的效能

55

SQL 效能

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

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

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

56

SQL 效能 伺服器端游標

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

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

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

57

SQL 效能

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

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

58

SQL 效能 查詢與搜尋限制

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

59

SQL 效能

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

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

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

60

SQL 效能

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

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

62