2011-06-22 62 views
11

假設我有這樣的要求:
系統中的對象都來自名爲IObject的基類,並且它可能具有帶顏色的對象,帶有轉換的對象以及兩者。接口作爲一種功能或接口作爲類型

現在有兩種方法來設計類層次結構。
第一個是:

只是讓來自 IObject提取派生具體類,並選擇「性能」 接口作爲其基類 表明它支持這樣的行爲, 一樣的界面:IHasColor,
IHasTransformation

第二個是:

組織的基類,並讓 從 一個由其衍生的具體類:IObject提取,IColorObject, ITransfromationObject, IColorAndTransformationObject

我更喜歡第一個(它有沒有一個正式的名字? ),因爲它更靈活,正如你可以看到第二個可能有類組合爆炸問題,當有許多屬性如顏色,轉換...

我想知道您的想法和建議。

謝謝。

+0

爲什麼之前的答案被刪除? –

+0

這個問題是__not language agnostic__如果是的話,你可以考慮使用mixins這些更優雅的替代方法,它根據你描述的語言的本質(使用接口,我假設C#或java?)是根本不存在的 –

+0

@Pablo Fernandez mixin是我認爲第一個解決方案的名字 –

回答

8

類抽象類型的對象的真實概念

接口抽象行爲或對象的真實概念

所以問題就變成了,是對象的「顏色」屬性還是對象的能力?

當你設計一個層次結構時,你將世界限制在一個更狹窄的空間。如果將顏色作爲對象的屬性,那麼您將需要種類的對象,那些有顏色的對象,沒有的對象。這是否適合你的「世界」?

如果你將它建模爲一個能力(接口),那麼你將擁有能夠向世界提供的對象,可以說是顏色。

對於轉換,同樣的邏輯適用。你可以把世界分爲兩類,一類可以轉化,一類不能,或者你可以把它看作是一種能力,一種物體可能有能力將自己轉化爲另一種東西。

對我來說,從這個角度來看,什麼是有意義將是:

  • 顏色是對象的屬性。事實上每個物體都應該有一種顏色,即使它是透明的,即使是反射,即使它的「無」(好運算出真實世界中具有顏色=無的物體意味着什麼,但它仍然可能在你的意義上有意義程序邏輯)。
  • 轉換是一種能力,也就是界面,對象有能力做的事情,除此之外,對象可能或可能無法做到。
+0

正是我在寫我的答案時想的,但你用了更少更好的單詞:) – grapkulec

+0

非常雄辯放! – Mrchief

+0

謝謝,我認爲另一種想法是這樣的:當我們將對象分類到層次結構中的IA和IB時,重要的是沒有對象具有IA和IB的屬性,否則將存在鑽石繼承,以及組合爆炸(如果有很多IC,ID ..),在這種情況下,最好是作爲行爲接口來爲其他類提供能力,這更靈活,對嗎? –

1

我認爲你直接跳到接口,跳過類。它是否需要你的應用程序。有一個「IObject」接口?也許您的類層次結構中的「CObject」根類可能會對您有所幫助。

它認爲獲勝者是第一種解決方案,你可能有一個「MyObject」,無論是接口的實現還是直接的類。稍後,您可以根據需要在您的類層次結構中添加其他類或接口。我認爲應該有一個「我的自定義應用程序類層次根對象」或「我的自定義應用程序類層次根接口」設計模式。在看到幾個應用程序(一些我的,一些其他)後,我認爲應該有一個「我的自定義應用程序類層次根對象」

1

我正在處理我的項目中的類層次結構,基本上我有類似的情況,就像你在你的問題中描述的那樣。

比方說,我有基本類型的對象,它是我的工具包中所有其他類的絕對根。所以自然,一切都是直接或通過它的子類派生的。每個Object-derived類必須提供一個通用的功能,但在某些葉類中,效果與其他類中的效果稍有不同。例如,每個對象的大小和位置都可以通過屬性或方法來改變,比如Position = new Point(10,10),Width = 15等等。但是有些類應該忽略屬性的設置或者根據自己修改內在狀態。想想停靠在父窗口左側的控件。你可以設置所有你喜歡的Height屬性,但它通常會被忽略,因爲這個屬性真的取決於父容器控件的高度(或者它的ClientArea高度或者像這樣)。

因此,在您需要「自定義」行爲的地方,實現基本通用功能的Object抽象類就可以了。如果Object提供在Height屬性的setter中調用的受保護的虛擬SetHeight方法,則可以在您的DockedControl類中重寫該方法,並且僅在停靠爲無時才允許更改高度,而在其他情況下,您可以限制或完全忽略。

所以我們很高興,但現在我們需要對Click,Hover等鼠標事件作出反應的對象。所以我們從抽象的Object類派生MouseAwareObject並實現事件和東西。

現在客戶端需要可停靠鼠標的對象。所以我們從DockableObject派生出來......嗯,現在呢?如果我們可以做多重繼承,那麼我們可以做到,但是我們遇到了鑽石問題,而且重複的界面不明確,我們需要處理它。在新的類和代理外部調用中,我們可以使用兩個Dockable類和MouseAware類來提供功能。

最後想到的是製作IDockable和IMouseAware接口,並讓它們定義功能並將它們自由添加到需要傳遞具體行爲/實現的對象中。

我想我會拆分我的基類成部分,並留下我的對象具有非常有限的「核心」屬性和方法集和其餘的功能,實際上是對象的可選項作爲類型,但在具體情況下需要移動到像IResizable,IDockable,IMakeAWorldABetterPlaceAble等接口。通過這種解決方案,可以將行爲「附加」到Object派生類,而無需將Draggin虛擬或純虛擬(抽象)方法從根基類一直拖到葉類。

在所有受影響的類中實現接口當然是不方便的,但是您始終可以實現一些「適配器」並僅將呼叫轉發給它們。這樣你就沒有重複的實現(當然有一些延伸),並且在任務的實現(Resize可能意味着不同類的不同事物)和期望的客戶端代碼之間解耦。

也許這不是你的問題的理想答案,但也許它會暗示你自己的解決方案。

+0

謝謝,grapkulec –