C#中的對象可能具有實際上是不同類別組合的功能;這方面的一個典型的例子是教師例子:
在這個例子中,教師有一個人(比如眼睛的顏色)的特徵(雖然我有一些教師可能會打破這個例子)和僱員的特徵(例如薪水)
C#不允許多重繼承,所以我們可以看看使用接口的想法composition。它往往是這樣描述的:
繼承意味着,如果貓的動物,然後貓繼承「是一個」動物
組成意味着,如果Cat實現噪音,那麼貓「有,一個」噪聲
爲什麼這個區別很重要?好吧,想象我們的貓。我們實際上可以讓Cat繼承自Feline,而Feline繼承自Animal。有一天,我們決定支持其他類型的動物,所以我們決定修改動物,但是後來我們意識到我們將會把這些變化推向其他所有的動物類型。我們的設計和層次結構突然變得相當複雜,如果我們一開始就沒有正確的做法,我們必須廣泛地重新設計我們所有的孩子班。
規則是Prefer Composition over Inheritance,但請注意Prefer這個詞的用法 - 這很重要,因爲它並不意味着我們應該只使用組合,而是我們的設計應該考慮繼承何時有用以及何時組合也很有用。它也提醒我們,在基類中堅持每一種可能的方法是一個壞主意。
我喜歡你的形狀的例子。試想一下:
我們ShapeSorter可能有這樣的方法:
public bool Sort(Shape shape)
{
foreach(Hole hole in Holes)
{
if(Hole.Type == HoleType.Circular && shape is Circle)
{
return true;
}
if(Hole.Type == HoleType.Square && shape is Square)
{
return true;
}
if(Hole.Type == HoleType.Triangular && shape is Triangle)
{
return true;
}
}
return false;
}
或者,我們可以做控制的一些輕微的反轉:
public bool Sort(Shape shape, Hole hole)
{
return hole.Accepts(shape); //We end up pushing the `if` code into Hole
}
或其某種變體。我們已經編寫了很多依賴於我們知道確切形狀的代碼。想象一下,它會多麼乏味維護,當你有其中之一:
所以,相反,我們認爲自己 - 有沒有我們可以通過蒸餾下來的相關說明我們的問題一個更通用的方法屬性?
你已經把它稱爲IShape的:然後
public interface IShape{
double Area {get;}
double Perimeter { get; } //Prefer Perimeter over circumference as more applicable to other shapes
int Sides { get; }
HoleType ShapeType { get; }
}
我們的排序方法將成爲:
public Hole Sort(IShape shape)
{
foreach(Hole hole in Holes)
{
if(hole.HoleType == shape.ShapeType && hole.Area >= shape.Area)
{
return hole;
}
}
return null;
}
它看起來整潔,但是是不是真的任何可能不會被通過做直接形狀。
事實是,沒有事實。最常見的方法將涉及使用繼承和組合,因爲現實世界中的許多事物都將是一種類型,並且還將具有由接口最好地描述的其他屬性。要避免的最重要的事情是堅持每一種可能的方法在基類中,並且擁有巨大的if語句來確定派生類型可以做什麼和不可以做什麼 - 這是很多難以維護的代碼。另外,在基類中加入太多的功能可能會導致您的代碼具體化 - 您將不會因爲所有潛在的副作用而後來修改代碼。
接口只聲明合同。所以客戶可以依靠合同,而不是特定的實施。 – zerkms
它可以。實際上,您正在使Circle類定義和實施該區域。 –