軟體開發中的矛盾——一個簡單的例子 (轉)

worldblog發表於2007-12-13
軟體開發中的矛盾——一個簡單的例子 (轉)[@more@]

在以前的文章中,我曾經提到過開發中充滿了矛盾,一些原則本身就是彼此矛盾的,需要不斷在這些矛盾中尋求折中、平衡。這裡給出一個源自實際的簡單例子,希望能給大家一點啟示,只是不知道是否貼切(說明:用C++語言描述,T為每個資料型別)。

在定義某個類的介面時,需要定義兩個相關變數a和b的getter/setter。為了使介面儘量精簡,我們採用第一種方法,用一對getter/setter來處理:

getAB(T* pa, T* pb);
setAB(T a, T b);

但是,如果有時只想對其中一個變數進行存取時,getAB/setAB就需要額外的工作,client方的程式碼就會很累贅。

為了獲取a的值,不得不定義一個額外的變數b:
T a, b;
getAB(&a, &b);
cout << a; // only get a

為了設定a的值而保留b的值,不得不額外呼叫getAB:
T a, b;
getAB(&a, &b);
a = 10;
setAB(a, b); // only set a

為此,我們採用第二種方法,將之拆成兩對函式:

T getA();
T getB();
void setA(T a);
setB(T b);

這樣,介面就一下擴充套件了一倍。但是,事情並未就此結束。有時,像getAB/setAB這樣的方式並非只起到了簡化介面的作用。在呼叫setAB的時候,我們可以從a和b相關的角度來考察a和b的合法性,比如:a,b代表某個值域的上下限,那麼假定如果a > b時,設定就不合理,就應該拒絕。而這種合法性檢查用第二種方法實現的時候就不是那麼順利了,粗看起來程式碼應該如下:

void setA(T a)
{
  if (a > m_b)
  return; // error
  else
  m_a = a;
}

void setB(T b)
{
  if (b < m_a)
  return; // error
  else
  m_b = b;
}

如果只是設定a、b中的一個值,倒不會有任何麻煩,這種方法完全勝任。但是,如果同時設定呢?暫且不考慮a、b的初值應該如何取,比如某次對a、b的設定使a、b分別等於5、10,而再次試圖重設a、b為15、20時,問題就產生了:

setA(15);
setB(20);

結果變成了a = 5, b = 20(completely error)。

如果要得到正確結果,則需要顛倒呼叫setA和setB的次序。但是,如果a、b要分別設成1、6呢?亦即,為了保證成功設定,client程式碼需要十分小心,不同情況,採用不同的呼叫約定。對於上述情況,用第二種方法很難做到正確的合法性檢查,因為函式的signature決定了它無法得知相關的另一個值,從而不能做出正確判斷。

所以,事情的演變過程就是:
為了使介面精簡,我們選擇方法一;
為了不增加額外的客戶程式碼,我們選擇方法二;
為了進行合法性檢查,我們又不得不選擇方法一。
這裡總共出現了兩種方法,三個原則(“為了……”)。

而當我們最終決定選擇方法二時,還是有可能揹負著“增加額外的客戶程式碼”這樣的罪名。而如果你確實不想如此,或許你會將兩種方法結合使用,即把getA/setA,getB/setB,getAB/setAB統統定義為介面。可是,你又可能會被人指責為“介面混亂”,而且對於setA/setB而言,合法性檢查仍然是個問題。

結論:在有多種方法可供選擇時,存在不同的原則(選擇依據),針對實際的情況,我們需要作出決定。這種決定往往不會做到滿足所有原則,但一般它應該是最大限度的適合大多數情形。如果,實際的情況不能足以使你作出很肯定的判斷,那麼,恐怕只有習慣和直覺可以影響你的決定了。只是,或許以後你還會修改你的決定。

ps:為了說明方便,所以這裡選用了一個極為簡單的例子。坦白講,可能有誇大之嫌。實際情況下,對於這樣的“getter/setter”問題,你多半不會像文中說得那樣處於如此為難的境地,除非你是個完美主義者。你可以很快作出決定,因為即使無法滿足某條原則,其代價也不會很高。否則,那些軟體開發人員,每天就不用寫幾行程式碼了,而且會深陷於矛盾的痛苦之中。但是,在這樣細微的地方都會存在矛盾,可以想見,用“矛盾重重”來形容軟體開發過程,可能是不算誇張的。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992790/,如需轉載,請註明出處,否則將追究法律責任。

相關文章