分類: coding
2013-07-31 23:21
550人閱讀
收藏舉報
coding
好的**總是將複雜的邏輯以分層的方式降低單個層次上的複雜度。複雜與簡單有乙個相互轉化的過程。
在涉及編碼解析的功能時,常常有乙個帶有長長一串case的switch,而且會不斷增長。為每乙個case搞個類就太誇張了,還是用表驅動(table driven)來取代它吧。這種應用已經太多了。而讓大家不去用的原因可能就乙個,認為表無法表達出屬性的差異。
比如一般而言有下面這樣乙個操作,大家肯定會覺得用表處理沒有問題。
operator function
add(x,y) addfunctionptr
sub(x,y) subfunctionptr
但是如果增加了乙個inc(x),它相對於先前兩個各有兩個引數就不一樣了。這就會被認為提取共性失敗,而放棄了表驅動法,又栽到switch..case的汪洋大海。
表驅動法就是兩個過程: i. 提取共性,做到為每個元素做一樣的事。把共性放到表中, 用查表法取代switch。 ii. 提供支援函式,因為提取共性後會要求有為這些共性服務的函式。第1步是比較簡單的,把第2步想透了才會提公升使用表驅動法的層次。
表驅動法,一方面簡化**實現,便於維護。(這裡的簡化強調地是上層實現邏輯的簡化。) 二是提高**的彈性,增減內容的成本低,甚至可以做到動態支援。
用乙個簡單的定義表驅動法的使用:
consttabletable = ,
};void handlingfunction(id)
} }
對應於上面兩個要點,就是table的定義和callhandlefunctionfortherow的寫法。 callhandlefunctionsforrow為每一行的處理提供了乙個一致的入口,這是表驅動法實現的關鍵,其原則為對每個元素都做同樣的事情,這裡同樣的事情應至少要理解為介面一致!下面討論兩個在實踐中常見的問題:相容不同資料型別和相容不同引數個數。
像前面的例子,只要讓function的定義相容兩個引數和乙個引數的情況就可以了。使用模板是乙個最佳解決方法,就是比較複雜一些。這個解決方案最後稍帶說一下。
先舉個例子說明一下公共函式的實現思路。 乙個維護一串屬性的類有三個方法,乙個寫值,乙個取值,還有乙個序列化函式(儲存和載入)。可以想像,如果加乙個屬性,就要增加至少三處**。(下面的示例沒有涉及序列化。)
下面是乙個向特別key寫入其值的操作,key代表的資料型別可能會不一樣, 如下示例(只用說明要點):
string getkeyvalue(string&key)return value; }
使用表驅動法,最簡單的一種方法就是用函式過載來處理型別不同的情況。可以使用類似如下的方法實現:
//表的定義static propertytablevalue propertytable = , ,
};//寫值,過載兩個函式來實現
bool
settings::setvalue(property_id id, const
char *value)
bool settings::setvalue(property_id id, int value)
//取值
int settings::getintvalue(property_id id)const
char* settings::getstringvalue(property_id id)
這樣的**,只是勉強實現了表驅動的功能。觀察**可以看到仍然有兩個明顯的問題:
1. 表的定義太死板,缺少靈活性。在定義表時分配記憶體問題明顯。
2. setvalue和getvalue仍然有不少重複的**邏輯。
針對第乙個問題,可以使用智慧型指標和兩個表維護的介面來解決,實現動態增減屬性。針對第二個問題,是典型的c++多型問題,可以使用模板類來實現。即實現乙個代表值的類,將取值操作交給這個類完成。 如:
templateclass settingvalue;
public:
t getvalue() ;
bool setvalue(t newvalue)
private:
t value;
};
對於字串的類,還需要特化處理,包括記憶體**和賦值操作。下面給出乙個賦值操作的特化實現:
template <>bool settingvalue<
char *>::setvalue(
char * newvalue)
strcpy(
value,newvalue);
return
true; }
在這個基礎上,要實現動態表就更容易了。下面實現出另外乙份setting manager:
struct propertytablevaluev2 ;static propertytablevaluev2 propertytable = , ,
}; class settingsv2
vpointer->setvalue(value);
return
true; }
template
t getvalue(property_idid)
};///[horky]省略部分**///
};
*注意在使用getvalue時要在函式後面加型別約束,如下:
printf
("settingsv2: id1 is %d\n"
, (mysetting.
getvalue
<
int>(
id1)));
printf
("settingsv2: id2 is %s\n"
,(mysetting.
getvalue
<
char
*>(
id2)));
到這裡,實現了乙個比較正常的表驅動方法了。但就問題而言,還至少有兩個化點:
a. 實現動態表。進一步降低屬性變化的成本,也更符合封裝的原則。
b. 將列舉id改為字串屬性,即以字串為id (以hash方式處理,不會明顯增加執行成本), 更加靈活。特別有利於序列化可讀的文字。
如果要再進一步,還可以增加乙個key的型別,來實現出可巢狀的引數設定。比如:
id2: (int)0
id2: (string)xhorky***
id3: (key)
id3_1:(int)1
id3_2:(float)0.3
針對開篇提到的問題,其實現方式也是使用模板類實現,貼出支援兩個引數的類定義就能理解了(如果只是針對這個例子,就有點殺機焉用宰牛刀的感覺!webkit的**裡這類似的應用。):
template:class operatorwithparameter2
private:
operatorwithparameter2(method method, param1 parameter1, param2 parameter2)
: m_method(method)
, m_parameter1(parameter1)
, m_parameter2(parameter2)
virtual
void perform()
private:
method m_method;
p1 m_parameter1;
p2 m_parameter2;
};
18章表驅動法
18章表驅動法的使用 1.含義 表驅動法 其實是一種程式設計模式 從表裡面查詢資訊而不使用邏輯語句 我的理解是 遇到複雜的邏輯時,考慮把邏輯資料存放到表中,通過查詢表來解決,不用通過寫複雜的 if else來解決,而且這樣迭代性 維護性也好。2.使用表啟動法的兩個問題及解決思路 1 如何去訪問表 1...
程式設計原則 表驅動法
說明這個概念之前最好先給出不使用這個概念的 常見的需要使用表驅動的場景有如下三種情況 場景1 不同條件不同資料 if key key a else if key key b 場景2 不同條件不同行為 if key key a else if key key b 場景3 不同條件依次執行 執行 key...
linux裝置驅動的難點重點
linux裝置驅的學習是一項浩繁的工程 l編寫linux裝置驅動要求工程師有非常好的硬體基礎,懂得sram,flash,sdram,磁碟的讀寫方式,uart,iic,usb等裝置的介面以及輪詢,中斷,dma原理,pci匯流排的工作方式以及cpu的記憶體管理單元 mmu 等。l非常好的c語言基礎,能夠...