2012-10-17 30 views
0

說我有一個A級「是的instanceof」界面設計不好

class A 
{ 
    Z source; 
} 

現在,上下文告訴我,「Z」可以是沒有按」不同類別(比如,B和C)的實例在他們的繼承樹中共享任何普通的類。

我猜想,天真的做法是讓'Z'成爲一個接口類,並使類B和C實現它。

但是仍然有些東西不能說服我,因爲每次使用類A的實例時,我需要知道'源'的類型。所以所有在多個「ifs」中做完的都是「instanceof」,這聽起來不太好。也許將來有一些其他類實現Z,並且硬編碼這種類型的ifs肯定會破壞某些東西。

問題的癥結在於,我無法通過向Z添加函數來解決問題,因爲在Z的每個實例類型中完成的工作是不同的。

我希望有人能給我和建議,也許關於一些有用的設計模式。

感謝

編輯:工作「的人」確實在一些地方,當得到A的一些實例是完全不同的視接口Z.這就是問題背後的階級,即是否「重要的工作實體'不是Z,是別人想要知道誰是Z.

EDIT2:也許一個具體的例子,將有助於:

class Picture 
{ 
    Artist a; 
} 

interface Artist 
{ 
} 

class Human : Artist { } 

class Robot : Artist {} 

現在的地方我有Picture一個實例,

Picture p = getPicture(); 
// Now is the moment that depending if the type of `p.a` different jobs are done 
// it doesn't matter any data or logic inside Human or Robot 
+0

請問您能詳細說明爲什麼您真的需要知道Z的真實類型?只要你知道它的接口(可用的方法等),你可以做你需要的一切。 –

+0

我添加了一些額外的信息。 –

+0

@DanielMonteiro:在很多情況下,人們會收集一些對象,其中一些會繼承或實現'Foo',其中一些不會,另一種願望希望在集合中的所有內容上執行一個動作可以爲所有對象定義,但可以通過'Foo'實例更好地完成。在這種情況下使用'instanceof'在我看來完全合適。 – supercat

回答

6

使用接口的目的是爲了隱藏這些不同的實現; A應該知道方法的意圖或高級目的。

Z的每個實現所做的工作可能不同,但用於調用該工作的方法簽名可能是相同的。類A只能調用方法Z.foo(),根據Z的實現是B還是C,將執行不同的代碼。

唯一需要知道實際實現類型的時間是當您需要對兩種不同類型執行完全不相關的處理時,它們不共享接口。但是在那種情況下,爲什麼他們被同一個A類處理?現在,有這可能是有意義的情況下,例如BC是從XML架構生成的類,並且您不能修改它們 - 但通常表示可以改進設計。

已更新現在您已添加圖片示例。我認爲這證實了我的觀點 - 雖然getPicture()的實施是不同的,目的返回類型是相同的。在這兩種情況下,藝術家都會返回一張圖片。

如果調用者想要以相同的方式處理機器人創建和人造圖片,則他們使用Artist界面。他們不需要區分人或機器人,因爲他們只是想要一張照片!創建圖片的細節屬於子類,調用者不應該看到這些細節。如果主叫方關心的是如何創建一張圖片,則主叫方應該畫出它,而不是機器人或人類,並且設計會有很大不同。

如果你的子類進行完全不相關的任務(這不是你的藝術家例子顯示了!),那麼你可以使用一個非常模糊的接口,比如標準的Java Runnable;在這種情況下,調用者真的不知道run()方法會做什麼 - 它只知道如何運行Runnable

鏈接

以下問題/報道來看,一些替代instanceof

而下面的文章也給出了示例代碼,使用這個例子看起來很像你:

和下面的文章中討論的instanceof與其他方法的利弊,如Visitor模式和非循環參觀者:

+0

但是Z.foo()的工作與Z背後的類無關。 –

+0

儘可能地,與Z背後的類有關的工作應該在Z後面的類中!OOP的原則之一。你能舉一個你想到的是什麼樣的課程和工作的例子嗎? – DNA

+0

我的情況很複雜,我添加了一個更簡單的例子 –

1

我認爲你需要發佈更多信息,因爲我認爲它是對OOP原則的誤解。如果你使用了一個通用的接口類型,那麼通過Liskov替換原則,它應該與哪個類型的源無關。

+0

這正是我所懷疑的,它在開始時並沒有很好的設計。但我也不知道該怎麼做。一個只有且只有一個類組的實例不共享任何邏輯的類的類。 –

1

我會打電話給你的A,B和C類Alpha,Beta和Gamma。

也許Alpha可以分成兩個版本,一個使用Betas,另一個使用Gammas。這可以避免Alpha中的instanceof檢查,正如你猜測的那樣的確是一種代碼異味。

abstract class Alpha 
{ 
    abstract void useSource(); 
} 

class BetaAlpha extends Alpha 
{ 
    Beta source; 
    void useSource() { source.doSomeBetaThing(); } 
} 

class GammaAlpha extends Alpha 
{ 
    Gamma source; 
    void useSource() { source.doSomeGammaThing(); } 
} 

其實這是非常普遍的。考慮一個Stream類的更具體的例子,它可以使用Files或Sockets。就這個例子而言,File和Socket不是從任何公共基類派生的。事實上,他們甚至可能不在我們的控制之下,所以我們不能改變它們。

abstract class Stream 
{ 
    abstract void open(); 
    abstract void close(); 
} 

class FileStream extends Stream 
{ 
    File file; 
    void open() { file.open(); } 
    void close() { file.close(); } 
} 

class SocketStream extends Stream 
{ 
    Socket socket; 
    void open() { socket.connect(); } 
    void close() { socket.disconnect(); } 
} 
+0

可能還需要一些區分Betas和Gammas的方法來決定是創建一個BetaAlpha還是一個GammaAlpha - 但這大概可以通過工廠方法或單個'instanceof'完成......? – DNA