2013-05-19 24 views
5

我相信這之前必須一直在問,但我似乎無法找到一個類似的例子。我非常瞭解多態性與方法重載,但這裏是一個可以解決的看似簡單的場景,我想不起來了:多態性處理參數 - 簡單的OO?

,讓我們說,我有幾個派生類的基類。我會用形狀在這個例子

base Shape 
derived Circle extends Shape 
derived LineSeg extends Shape 

現在,形狀有一個名爲方法相交(其他),對另一種形狀的測試,看看他們是否相交。使用多態性很容易看到Circle,LineSeg等如何實現自己的「相交」方法,並且通過方法重載,我可以輕鬆實現所有需要的組合。例如,

Circle.intersect(LineSeg) 
Circle.intersect(Circle) 
LineSeg.intersect(Circle) 

到目前爲止好。

的問題是,如果我保持形狀的中心名單,我想這樣做:

for some shape s 
Foreach shape in Shapes 
    if (s.intersect(shape)) - do something 

目前,我不知道這是怎麼可能的,因爲方法的重載選擇「相交」方法來匹配基本類型Shape,而不是適當的參數類型。如何在沒有if-else鏈的情況下檢查類型和向下轉換?

順便說一句,我使用Java,但我不知道該語言是完全相關的,因爲它似乎是一個基本的設計問題。似乎很簡單,我錯過了什麼?

謝謝!


解決下面(謝謝!),看到細節那裏。基本上,由具有在派生類然後調用適當的方法(一個訪問者模式?)回調,則可以使用「這」關鍵字調用適當的交叉方法,因爲它具有所需的正確類型。

+1

有些編程語言支持的論點運行時類型更直接超載。例如,LISP支持* multimethods *,並且我認爲一些函數式編程語言支持相同。在Java中,這通常稱爲* double dispatch *,而GoF Visitor模式則是要走的路。 –

+1

不要挑剔你的設計,但你的繼承有點不對。線段並不是真正的形狀。他們組成形狀。你會檢查任何一個很多的交集,這取決於形狀,組成這兩個形狀的線段:) – ChiefTwoPencils

+0

@ C.Lang,很好的捕獲,但如果我將lineSeg重命名爲「牆」,會怎麼樣?多數民衆贊成基本上是如何使用它,但稱它lineSeg使它更容易概念化使用它在其他更復雜的形狀 – user1922401

回答

3

我的第一個想法是訪問者模式,幾乎給每個Shape兩個方法,一個我會在每個Shape類型上調用intersect(Shape)和一個doIntersect()方法。

它看起來大約是這樣的:

interface Shape { 
    public abstract Intersection intersect(Shape other); 

    public abstract Intersection doIntersect(Circle circle); 

    public abstract Intersection doIntersect(LineSeg line); 
} 
class LineSeg implements Shape { 
    @Override 
    public Intersection intersect(Shape other) { 
     return other.doIntersect(this); 
    } 

    Intersection doIntersect(Circle circle) { 
     // Code to intersect with Circle 
    } 

    Intersection doIntersect(LineSeg other) { 
     // Code to intersect with another Lineseg 
    } 
} 

class Circle implements Shape { 
    @Override 
    public Intersection intersect(Shape other) { 
     return other.doIntersect(this); 
    } 

    public Intersection doIntersect(Circle other) { 
     // Code to intersect with another Circle 
    } 

    public Intersection doIntersect(LineSeg segment) { 
     // Code to intersect with LineSeg 
    } 
} 

您可能希望在doIntersect方法進行包專用或雖比選擇這些不同的名稱。

+0

這太棒了,謝謝。快速,輕鬆地解決了我的問題。我需要在基類中添加所有參數類型的可能性作爲原型,強制兒童處理任何可能性。 – user1922401

+0

另外一個細化:你應該能夠命名'intersect()'的所有方法,而不是命名特定的'doIntersect()'。據我所知,編譯器會選擇最適合的特定超載。 – confusopoly

+0

謝謝 - 我已經這樣做了,但決定留下你的建議,因爲它可能不那麼容易混淆。我也試過把它放在基類中,以便只有一個是必需的,但在基類方法中使用「this」具有基類類型,不幸的是(我明白了爲什麼) – user1922401

0

瞭解泛型JDK5

對於形狀<每個形狀>

0

你的形狀類必須是一個抽象類,這意味着它不會被實例化,只有派生類圓形和lineseg有實例。相交方法應該是在虛擬的形狀,所以當你遍歷所有的形狀,每種形狀的交叉方法被稱爲

public abstract class Shape { 

    boolean intersect(Shape s); 
} 

public class Circle extends Shape { 

    boolean intersect(Shape s) { 
     ... 
     if(s instanceOf Circle) { 
      .... // Circle intersects cicrcle 
     } else if(s instanceOf Lineseg) { 
      .... // Circle intersects Lneseg 
     } else { 
      throw RuntimeException("Unrecognized shape"); 
     } 

    } 

} 

public class Lineseg extends Shape { 

    boolean intersect(Shape s) { 
     ... 
     if(s instanceOf Circle) { 
      .... // Lineseg intersects circle 
     } else if(s instanceOf Lineseg) { 
      .... // Lineseg intersects lineseg 
     } else { 
      throw RuntimeException("Unrecognized shape"); 
     } 
    } 

} 
+0

對不起,我要求的答案沒有那麼if-else類型檢查鏈 – user1922401

+0

@ user1922401但是,您願意爲每個派生類中的每個派生類編寫一個方法,這是相同的。只有這樣才能處理多態性的經典OO方式。 – ilomambo

+0

它是不一樣的,因爲通過訪問者模式你只有一個額外的間接。使用if-else鏈,每次調用該方法時最多有n次檢查。此外,建議的解決方案稍安全一些。如果我添加一個新類型,我必須將新類型的相交頭添加到基類,否則它不會編譯,並強制所有其他形狀類型來處理它 - 在您的解決方案中,我可能會意外地忘記它。雖然你打算在每個類中都有一個if-else,但這可能是集中的 – user1922401