2012-01-18 48 views
0

以下是什麼問題,以及如何使用面向對象原則更好地實現它?以適當的方式在課堂上設計出現問題

我的應用程序,包含了一些形狀類,所有從Shape繼承 - CircleRectangleTriangle等有些需要在屏幕上顯示,在這種情況下,他們需要利用普通屏幕的邏輯,所以有包含公共邏輯的ScreenShape超類,以及ScreenCircle,ScreenTriangle子類。

+2

你對這種設計的批評是什麼? – DOK 2012-01-18 18:29:45

+0

@DOK經理並不滿意......儘管他給了我一個設計方案,但他仍然表示你應該研究這個。 – user1147717 2012-01-18 18:33:22

+0

他有沒有說具體的事情?爲什麼他不滿意? – 2012-01-18 18:34:16

回答

3

我建議做一個界面形狀,它提供關於幾何形狀的基本藍圖,所有的類實現形狀界面,並創建一個單獨的類ScreenShape(或抽象類),所有類將擴展並提供在ScreenShape類的屏幕上顯示的方法。例如你的矩形類將會有點像這樣

class rectangle extends ScreenShape implements Shape 
{ 

// provide implementation of Shape interace methods. 


// over-ride ScreenShape methods 

public void draw() 
{ 
// actual logic of drawing the objects on screen 

} 

} 
1

我看到的問題是,您不能從Java中的多個類繼承(儘管您可以實現多個接口)。所以你最好將ScreenShape的功能合併到Shape中,假設ScreenShape在其方法中有一些具體的代碼。

+0

感謝您的迴應,您能否詳細說明應該是什麼接口以及應該是什麼超類? – user1147717 2012-01-18 18:37:49

+0

這種方法的問題是,每個擴展Shape的類現在都具有「屏幕」功能,根據我的理解,這不應該發生。 – 2012-01-18 18:39:08

0

從最通用到最具體的工作。

所以它應該是這樣的:

。形狀(包含存在任何種類的「形狀」代碼 - 說不定一個抽象類或接口)

.. ScreenShape(含有邏輯用於繪製在屏幕上)

...圈(含有邏輯繪製在屏幕上的圓)

...廣場(含有邏輯用於繪製在屏幕上爲正方形)

所以:

public abstract class Shape { 
    // ... generic Shape stuff here, possibly an interface 
    public abstract void getCoordinates(); 
} 

public abstract class ScreenShape extends Shape { 
    public void drawOnScreen() { 
    // logic for drawing on a screen here 
    // likely invoking 'getCoordinates()' 
    } 
} 

public class Circle extends ScreenShape { 
    public void getCoordinates() { 
    // circle specific stuff here, implementation of stuff inherited from Shape 
    } 
    // also inherits whatever 'drawOnScreen()' implementation 
    // is provided in ScreenShape 
} 
+0

該解決方案提出了一個新問題:如果我需要一個可以是屏幕形狀或普通形狀的圓,該如何擴展?我應該做2個新班嗎? – Vlad 2013-08-13 12:29:47

0

您可以實現將實現公共邏輯的draw()方法。

public abstract class Shape{ 

    public void draw(){ 
     //common logic here 

     drawImpl(); 

     //more logic here if needed 
    } 

    public abstract void drawImpl();   

} 

然後你的實現將實現drawImpl類。

或者,您可以實現一個類ScreenBehaviour並使用組合。然後,每個實現可以使用不同的ScreenBehaviour實現這樣的:

public abstract class Shape{ 
    private ScreenBehaviour screenBehaviour; 

    public final void draw(){ 
     screenBehaviour.execute(); 
    } 
} 
2

經理與設計中的問題可能是你正在泄漏問題納入你的對象模型。你已經混合了形狀與渲染的概念。那麼現在如果你需要在繪圖程序中使用形狀呢?根據您目前的設計,你需要定義的PlottedShapePlottedCirclePlottedSquare等等等等

您應該保留形狀對象層次乾淨,再定義一個單獨的類,它可以處理圖形的渲染輸出設備。

1

關於您迴應張貼什麼@DOC:

@DOK經理不滿意......雖然他給了我前進的設計,但仍然他說你應該對這個研究。

我想你的經理並不滿足目前的設計的原因是因爲它是從一個叫Parallel Inheritance Hierarchiescode smell痛苦。基本上,它描述了你正在經歷的事情:每次創建一個新的子類時,你都必須在並行層次結構中創建它的對應子類。 這會讓你的設計變得更加困難,從而維持。

當前回答評論

我要評論一些事情,我沒有對當前的答案一致。 基本上你建議要麼:

  1. 繼承一個類ScreenShape
  2. 實施的Shape

每個子類繪製邏輯,我可以看到兩個問題與選項N°1打算:

  • 它的形狀定義爲一個接口,所以你必須在每個子類(Rectangle等)重新實現它
  • 爲了克服前面的情況,你可以使ScreenShape實現它,因爲所有形狀都將從它繼承。這是不好的,@Perception的答案解釋了爲什麼。
  • 假設您現在被要求將形狀導出到XML文件(這是對形狀的另一種操作,就像顯示它們一樣)。遵循這種方法,您需要在XMLShape類上實現該行爲並從中繼承,但您已經從ScreenShape繼承。看到問題了嗎?

選項N°2,迫使你與臃腫的關注呈現您的域模型,再次就像@Perception說:)

一些目標,實現

從前面的,我們可以看到即:

  • 當您的域模型更改時,您將希望您的類更改,而不是當您顯示形狀或將它們導出爲XML更改時。
  • 這會將您引導至Single Responsibility Principle,其中指出某個班級只能有一個更改理由。
  • 我們發現,顯示以及導出到XML(在我給出的示例中)是您將在形狀上執行的操作,您將希望稍後輕鬆添加新操作而不更改形狀類。

建議的解決方案

首先,讓你的Shape層次清理出來,並留下屬於您的問題域的唯一的東西。

然後,您需要不同的方式來顯示形狀,而無需在Shape層次結構中實現該行爲。如果您不會處理嵌套形狀(包含其他形狀的形狀),則可以使用名爲Double Dispatch的技術。它可以讓你發送通過編碼方法名派往收到這樣的論點,即信息基礎上,接收器的類型的方法調用:

public class Circle extends Shape { 
    public void DisplayOn(IShapeDisplay aDisplay) { 
     aDisplay.DisplayCircle(this); 
    } 
} 

public class Rectangle extends Shape { 
    public void DisplayOn(IShapeDisplay aDisplay) { 
     aDisplay.DisplayRectangle(this); 
    } 
} 

interface IShapeDisplay { 
    void DisplayCircle(Circle aCircle); 
    void DisplayRectangle(Rectangle aRectangle); 
} 

類實現IShapeDisplay將負責顯示各種形狀。關於這一點的好處是你設法清除Shape層級中的那些煩人的細節,並將它們封裝在自己的類中。因此,現在您可以在不修改Shape子類的情況下更改該類。 Refactoring: Improving the Design of Existing Code

最終意見

有關代碼味道和並行繼承層次在Martin Fowler的書你可以閱讀更多。

此外,如果您需要處理形狀嵌套:複合和訪問者模式,您想檢查Design Patterns: Elements of Reusable Object-Oriented Software書籍。

希望這對你有幫助!