簡單來說,表格驅動法是藉由直接在「表格」中尋找資訊,而不是用邏輯將其計算出來(if, case等)。
範例
當我們經營一間餐廳,每張桌子都有不同的座位數、沒有邏輯的時候,我們可能就會需要用到if-else來判斷:
if table number == 1 table has 4 seats else if table number == 2 table has 8 seats . . . // 如果只有5張桌子,那或許可以這樣做。但是如果有50張桌子,這邏輯語言就會顯得冗長許多,維護起來也較困難。
因此作者用array(陣列)去當作一個表格驅動法來解:
tables [] = {4, 8, 2, 4, ...} table seats = tables[table number] // 將Index作為座位編號,值代表座位數。
這麼一來程式碼就會變得又簡單又短,效率高且方便維護。它是一種直接使用「物件結構」來展示結果的方式。
補充參考資料>>
假如想要依字元區分出數字、標點及字母,可以看看下面的範例:
// 區分字母
if ( ( ( 'a' <= inputChar ) && ( inputChar <= 'z' ) ) ||
( ( 'A' <= inputChar ) && ( inputChar <= 'Z' ) ) ) {
charType = CharacterType.Letter;
}
// 區分標點
else if ( ( inputChar == ' ' ) || ( inputChar == ',' ) ||
( inputChar == '.' ) || ( inputChar == '!' ) || ( inputChar == '(' ) ||
( inputChar == ')' ) || ( inputChar == ':' ) || ( inputChar == ';' ) ||
( inputChar == '?' ) || ( inputChar == '-' ) ) {
charType = CharacterType.Punctuation;
}
// 區分數字
else if ( ( '0' <= inputChar ) && ( inputChar <= '9' ) ) {
charType = CharacterType.Digit;
}
如果改用表格查找,我們可以這樣簡化:
charType = charTypeTable[ inputChar ];
[補充] 我們將其放進我們的表格(這裡是以ASCII為例)
CharacterType[] charTypeTable = new CharacterType[128];
// 初始化查找的表格,用ASCII值判斷屬於哪種字符。
for (int i = 0; i < charTypeTable.length; i++) {
if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
charTypeTable[i] = CharacterType.Letter;
} else if (i >= '0' && i <= '9') {
charTypeTable[i] = CharacterType.Digit;
} else if (",.!?():-;".indexOf(i) >= 0) { // indexOf查無相符結果會回傳-1
charTypeTable[i] = CharacterType.Punctuation;
} else {
charTypeTable[i] = CharacterType.Other; // 其他字符類型
}
}
上述不論是哪一種,都會增加表格的複雜性。
與所有查詢表一樣,直接存取表代替了更複雜的邏輯控制,不必經過任何複雜的障礙,就可以找到所需要的資料。
顧名思義,直接存取表可以直接訪問指定的表格元素
當需要確定每個月份的天數,我們可以笨笨的用if-else來寫:(忽略閏年)
' ❌ 錯誤示範
If ( month = 1 ) Then
days = 31
ElseIf ( month = 2 ) Then
days = 28
ElseIf ( month = 3 ) Then
days = 31
ElseIf ( month = 4 ) Then
days = 30
ElseIf ( month = 5 ) Then
days = 31
ElseIf ( month = 6 ) Then
days = 30
ElseIf ( month = 7 ) Then
days = 31
ElseIf ( month = 8 ) Then
days = 31
ElseIf ( month = 9 ) Then
days = 30
ElseIf ( month = 10 ) Then
days = 31
ElseIf ( month = 11 ) Then
days = 30
ElseIf ( month = 12 ) Then
days = 31
End If
更簡單的方式是,將它放進array中:
**' 定義一個array**
Dim daysPerMonth() As Integer = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
**' 使用這個array**
days = daysPerMonth( month-1 )
假設撰寫一個程式來計算醫療保險費率,並且費率因年齡、性別、婚姻狀況跟是否吸煙而異, 用if-else邏輯寫會得到如下結果:
// ❌ 錯誤示範
if ( gender == Gender.Female ) {
if ( maritalStatus == MaritalStatus.Single ) {
if ( smokingStatus == SmokingStatus.NonSmoking ) {
if ( age < 18 ) {
rate = 200.00;
}
else if ( age == 18 ) {
rate = 250.00;
}
else if ( age == 19 ) {
rate = 300.00;
}
...
else if ( 65 < age ) {
rate = 450.00;
}
}
else {
if ( age < 18 ) {
rate = 250.00;
}
else if ( age == 18 ) {
rate = 300.00;
}
else if ( age == 19 ) {
rate = 350.00;
}
...
else if ( 65 < age ) {
rate = 575.00;
}
}
}
else if ( maritalStatus == MaritalStatus.Married )
...
}
這還只是限制了其中一部分的條件,你能想像當全人類都被劃入範圍後,那個邏輯有多麼複雜嗎?
我們當然可以把年齡放進陣列中,但更好的解法是:小孩才做選擇,我 們 全 部 都 放 進 去!!
Public Enum SmokingStatus
SmokingStatus_First = 0
SmokingStatus_Smoking = 0
SmokingStatus_Last = 1
SmokingStatus_NonSmoking = 1
End Enum
Public Enum Gender
Gender_First = 0
Gender_Male = 0
Gender_Last = 1
Gender_Female = 1
End Enum
Public Enum MaritalStatus
MaritalStatus_First = 0
MaritalStatus_Single = 0
MaritalStatus_Last = 1
MaritalStatus_Married = 1
End Enum
Const MAX_AGE As Integer = 125
Dim rateTable ( SmokingStatus_Last, Gender_Last, MaritalStatus_Last, MAX_AGE ) As Double
表格驅動法的優點是,可以直接將其儲存成一個檔案,當執行時需要再去讀取即可。
現在我們來使用它:
rate = rateTable( smokingStatus, gender, maritalStatus, age )
這個方式更方便閱讀、更容易維護及修改。
在前面的範例,至少我們知道還可以用if-else來解決,但是有時當資料過於複雜時,就無法單單只用if-else去解決。
範例
假設目前你要寫一個「列印檔案中的內容」的常式,這個檔案通常有500條訊息,且分成20種分類。
這些資料的來源是某個水桶的浮標,它會將水溫存入檔案中。每條訊息會有一個ID用來辨識是哪一種的分類的訊息。
有幾個先決條件:
因此我們可能會有以下的訊息資料:(除了ID以外皆每種訊息分類都可能有不同的格式)
解法1:基於邏輯的方法
假如今天又多了N種新的訊息分類,或者某個訊息改了型別,我們就得一一修改它。
以偽程式來看程式邏輯應該會是長這樣:
// 這是一段偽(代)碼 (pseudocode,詳見第9章)
While more messages to read
Read a message header
Decode the message ID from the message header
If the message header is type 1 then
Print a type 1 message
Else if the message header is type 2 then
Print a type 2 message
...
Else if the message header is type 19 then
Print a type 19 message
Else if the message header is type 20 then
Print a type 20 message
End While