一次成功的重構實踐3 - 抽象的藝術
黃國強 2019/2/7
抽象能力的培養非常重要。所謂抽象即抓住事物的本質規律,透過現象看本質。一個程序員工作多年,寫代碼的技術都會掌握。但是如果想做架構,缺乏抽象能力是不行的。
這就好比工匠和藝術家,前者只能做到是技藝純熟,不斷重複自己。藝術家往往可以表達人類普遍情感,探索客觀世界的規律。比如,物理學上補色原理就是印象派畫家最先發現並運用到他們的作品中。
回到編程,看一個具體的例子。
設備裏用到了多種IO控制卡,控制卡負責讀取傳感器輸入信號並輸出信號控制設備動作。下面分別是三個控制卡的API。
// 控制卡1的輸入輸出函數
EXPORTS void CALLBACK PIODIO_OutputWord(DWORD wPortAddress, DWORD wOutData);
EXPORTS void CALLBACK PIODIO_OutputByte(DWORD wPortAddr, WORD bOutputValue);
EXPORTS DWORD CALLBACK PIODIO_InputWord(DWORD wPortAddress);
EXPORTS WORD CALLBACK PIODIO_InputByte(DWORD wPortAddr);
// 控制卡2的輸入輸出函數
EXPORTS WORD CALLBACK FRB_SendSA(WORD wPort, WORD SAn, WORD wOutputData);
EXPORTS WORD CALLBACK FRB_ReceiveRA(WORD wPort, WORD RAn, WORD *wInputData);
EXPORTS WORD CALLBACK FRB_ReadRAStatus(WORD wPort,BYTE *bRAStatus);
// 控制卡3的輸入輸出函數
short __stdcall ps400_get_FRnet_DI
(
BYTE bCardID, /* the specific Card ID that is configured by onboard DIP-switch */
WORD wSA, /* Group Address FRNET_SA8 ~ FRNET_SA15 */
WORD *pStatus, /* the pointer to the FRnet DI status */
WORD wEnableDirectAccess = FRNET_ENABLE_DIRECT_ACCESS /* FRNET_ENABLE_DIRECT_ACCESS/FRNET_DISABLE_DIRECT_ACCESS, access the DI module directly, or read the stored status in driver */
);
short __stdcall ps400_set_FRnet_DO
(
BYTE bCardID, /* the specific Card ID that is configured by onboard DIP-switch */
WORD wRA, /* Group Address FRNET_RA0 ~ FRNET_RA7 */
WORD wDOData /* the data to DO module */
);
很明顯,三個API完全不同,任何其他模塊如果用到IO,並直接使用這些API的話,將不可避免地導致這些代碼不可移植,換IO卡必須重寫這些代碼,這很無聊。而且類似模塊如果很多的話,工作量也很大。
因而這裏我們需要抽象,我們認爲世界上只有標準的IO卡,這個IO卡包含下面幾個函數。
// 從衆多不同的IO卡提煉出以下三個基本函數成爲類成員
void BitOn(const WORD, const WORD, const WORD, const BOOL); // 輸出ON信號
void BitOff(const WORD, const WORD, const WORD, const BOOL); // 輸出OFF信號
bool Bits(const WORD, const WORD, const WORD, const BOOL); // 查詢當前IO狀態
問題來了,這些函數怎麼獲得呢?這裏加重點:要從軟件用戶的角度,獲得得到這些函數。以氣缸爲例,對於用戶來說,客戶只關心如何打開氣缸、如何關閉氣缸以及當前是關還是開的狀態。所以這個IO卡抽象出這個三個函數是正確的。如果站錯了角度,你抽象出的函數根本就是不對的。
我們把真實的IO卡適配到這個標準卡,達到設計模式裏所提到到的“抽象與實現分離”的目的。架構師的工作就是不斷髮掘系統裏形形色色的類和函數,把抽象出一個又一個與具體實現無關的類放到領域層中。那麼領域層到底是什麼?賣個關子,以後再寫。