原文見http://www.cnblogs.com/birdshome/archive/2005/02/25/108866.html
birdshome在原文中引用了我給他的一個測試代碼,但那個測試代碼原意只是為了說明在Derived類的Foo中調用base.Foo時,因為多態的關係,Derived的Bar會被調用,而Base類本身的介面設計是有問題的。
“其實這確實是物件導向中的一個設計矛盾,等於平白的無故的把一些語義規則強加給了程式員,而且還是相當隱諱的。”
在OOD和OOP中,最重要的是Thinking in Object,語言所提供的物件導向的功能,更多的是表達出來的一種語義,表達的是對象的一種特性。C#中的virtual,不僅僅表示某一個方法是一個虛函數,更重要的是,它為對象的方法添加了多態的能力,意味著類的設計者希望這個類能夠被派生,並且允許在衍生類別中對此方法進行重載。而此,語義的規則,反映的是程式員所設計的對象的特徵,而不是實現相關的。OOD的第一步是抽象,如果沒有抽象出類所代表的對象的特性,那麼在實現過程中,就會因為對語言功能的錯誤使用而產生具有錯誤語義的抽象。
回過頭來看Base類的介面設計,原來的代碼如下:public class Base
{
public virtual void Foo()
{
Console.WriteLine("Base::Foo");
this.Bar();
}
public virtual void Bar()
{
Console.WriteLine("Base::Bar");
}
};
從物件導向語義和對象介面設計的角度來看,Base類的介面設計是有很多問題的。
首先,Foo做為一個public的介面,卻調用了另一個public Bar方法,從介面設計上來說,是功能重疊,不滿足物件導向設計中的最小化介面要求。
其次,Foo是virtual方法,這意味著Foo的實現會被衍生類別重載,而在Foo的實現中,調用了方法Bar。衍生類別是沒有責任在Foo的重載中調用Bar方法的,這意味著基類的邏輯不具有傳遞性。
第三,Foo本身是virtual方法,而其調用的Bar也是virtual方法,這導致衍生類別具有過強的重載能力,這種能力常常會造成邏輯上的衝突。
因此,Base類的介面需要重新設計,可以參考Template Method模式,重構如下:public class Base
{
public void Foo()
{
this.DoFoo();
this.DoBar();
}
public void Bar()
{
this.DoBar;
}
protected virtual void DoFoo()
{
}
protected virtual void DoBar()
{
}
};
當然,真正適合的介面設計要參照應用的情境,是不能簡單的通過一個範例程式碼來說明的。
而原文的問題,是因未做if (obj != null)的測試而引起的,因為Bar本身也是一個public方法,能夠被其他方法調用。從Derived類的介面設計上來看,沒有Foo一定在Bar調用前的語義,而此必要的判斷是必需的。