2012-01-18 77 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书籍。

希望这对你有帮助!