0

以下有什么问题,我将如何使用 OO 原则更好地实现它?

我的应用程序包含一堆形状类,它们都继承自Shape- Circle, Rectangle,Triangle等。其中一些需要显示在屏幕上,在这种情况下它们需要利用通用屏幕逻辑,所以有一个ScreenShape包含通用逻辑的超类,和ScreenCircle,ScreenTriangle子类。

4

6 回答 6

3

我建议制作一个接口 Shape ,它提供有关几何形状形状的基本蓝图,并且您的所有类都实现 shape 接口并创建一个单独的类 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

}

}
于 2012-01-18T18:36:15.937 回答
2

您的经理在设计中遇到的问题可能是您将关注点泄漏到您的对象模型中。您将形状的概念与渲染方式混合在一起。那么现在如果您需要在绘图程序中使用形状怎么办?使用您当前的设计,您需要定义 a PlottedShapePlottedCirclePlottedSquare

您应该保持 Shape 对象层次结构干净,然后定义一个单独的类来处理将形状渲染到输出设备。

于 2012-01-18T18:41:04.277 回答
1

我看到的问题是您不能从 Java 中的多个类继承(尽管您可以实现多个接口)。因此,假设在其方法中有一些具体代码,您最好将功能合并ScreenShape到 into中。ShapeScreenShape

于 2012-01-18T18:34:12.090 回答
1

关于您在回复@DOC 时发布的内容:

@DOK 经理不满意......虽然他让我继续设计,但他仍然说你应该对此进行研究。

我想您的经理对当前设计不满意的原因是因为它受到称为Parallel Inheritance Hierarchies的代码气味的困扰。基本上,它描述了您正在经历的事情:每次创建新的子类时,您都必须在并行层次结构中创建其对应的子类。这将使您的设计更难更改,从而更难维护。

当前答案审查

我想评论一些我不同意当前答案的事情。基本上,建议您:

  1. ScreenShape类继承
  2. 在每个子类中实现绘图逻辑Shape

我可以看到选项 n° 1 存在两个问题:

  • 它将 Shape 定义为一个接口,因此您必须在每个子类(Rectangle等)中重新实现它
  • 为了克服以前的问题,您可以ScreenShape实现它,因为所有类型的形状都会继承自它。这很糟糕,@Perception 的答案解释了原因。
  • 假设您现在被要求将形状导出到 XML 文件(这是对形状的另一个操作,就像显示它们一样)。按照这种方法,您需要在一个XMLShape类上实现该行为并从它继承,但您已经从ScreenShape. 看到问题了吗?

选项 n° 2 迫使您用表示问题来膨胀您的域模型,就像@Perception 所说的那样:)

一些要实现的目标

从前面我们可以看出:

  • 您会希望您的类在您的域模型发生变化时发生变化,而不是在您显示形状或将它们导出到 XML 的方式发生变化时发生变化。
  • 这就引出了单一职责原则,该原则指出一个类应该只有一个改变的理由。
  • 我们发现显示以及导出到 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子类的情况下更改该类。

最后的评论

您可以在 Martin Fowler 的书:重构:改进现有代码的设计中阅读有关代码异味和并行继承层次结构的更多信息。

此外,如果您需要处理形状嵌套:复合和访问者模式,您还想查看设计模式:可重用的面向对象软件的元素一书。

希望对你有帮助!

于 2012-01-19T04:58:30.020 回答
0

从最通用到最具体的工作。

所以它应该是这样的:

. 形状(包含任何类型的“形状”的代码 - 可能是抽象类或接口)

.. ScreenShape(包含在屏幕上绘制的逻辑)

... 圆(包含在屏幕上绘制圆的逻辑)

... Square(包含在屏幕上绘制正方形的逻辑)

所以:

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
}
于 2012-01-18T18:38:18.463 回答
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();
    }
}
于 2012-01-18T18:40:02.077 回答