【问题标题】:Inheritance vs Composition for canvas shapes model?画布形状模型的继承与组合?
【发布时间】:2013-03-15 12:33:16
【问题描述】:

对于以下模型,您更喜欢继承还是组合:

  • 我想在canvas 上绘制对象,每个对象代表一个数据对象
  • 把它想象成一个状态机图:Ellipses 代表StatesLines 代表它们之间的connections/transitions。对象表示本身永远不会改变,即State 将始终由ellipse 显示。但是ellipse 的绘制方式应该不同,例如对于选择它应该有不同的颜色,而拖动它应该有一个 alpha 通道等。

从设计的角度来看,ellipse 不是stateline 不是transition。无论如何,将这两个对象组合起来是合适的,以便能够将它们收集在 List<Shape> 中并在每个对象上执行 shape.draw()

现在有 2 个设计模型是可能的,而我认为 2 个类总是相同的:

interface Shape {
    void draw();
}
abstract class Figure implements Shape {
    //basic vars like start and end coordinates
    int x0, y0, x1, y1;
}

继承:

abstract class State extends Figure {
    String name;
}
class Rectangle extends State {
    @Override void draw();
}

class Line extends Figure;
class Transition extends Line

虽然从设计的角度来看,rectangle 不是Statestate 也不是figure,但关于绘图上下文,这可能是可行的。因为我可以继承处理shapes、绘图等所需的大部分内容。

或作文:

abstract class State {
    String name;
}

class Rectangle extends Figure {
    private State state;
    @Override void draw();
}
class Line extends Figure {
    private Transition transition;
    @Override void draw();
}

所以Rectangle + Line 将是我的对象的包装器。

一个 Rectangle 和 Line 应该扩展 State 和 Transition,还是包含它?

或者,也许还有我没有看到的第三种设计选项。期待您的想法。

【问题讨论】:

  • 我使用 Eclipse 建模框架 (EMF) 做了类似的事情,即使用模型驱动技术。您将为数据创建一个域模型,一个用于图形表示,一个连接两者的元素。这需要一些纪律,但最终确实可以得到回报。

标签: java oop java-canvas


【解决方案1】:

首先,我认为您的建模不正确,椭圆不代表状态,但状态由椭圆表示。正如你所说的那样,状态不是椭圆。所以你应该有构图,我会选择 State 对象来拥有代表它的 Shape。

abstract class State
{
    private Shape shape;
}

要使形状可选择并仍然使其远离状态抽象,那么应该有一个可选择形状的接口。

interface SelectableShape
{
    void setSelected();
    void setUnselected();
}

除了上述之外,如果你想让例如矩形通过过渡连接到另一个矩形,那么你需要声明额外的接口,例如 ConnectableShape 和 ShapeConnection。

interface ConnectableShape extends 
{
}

interface ShapeConnection
{
    void connect(ConnectableShape sh1, ConnectableShape sh2);
}

此外,状态有责任了解它们的转换(或转换有责任了解它们连接的状态),必须用 Shape、ConnectableShape、ShapeConnection 抽象出来的东西是这一切的呈现。因此,做如下的事情是不错的设计。

abstract class State
{
    private ConnectableShape shape;
    public ConnectableShape getShape() {
        return shape;
    }
}

abstract class Transition
{
    private ShapeConnection conn;
    public void connect(State s1, State s2) {
        conn.connect(s1.getShape(),s2.getShape());
    }
}

【讨论】:

    【解决方案2】:

    所以,这是我的想法,但对于大多数设计问题,很少有一个“正确”的答案。

    就像你说的,State 不是 Rectangle,Transition 也不是 Line。我们可以画一条线和/或一个矩形,类似地对待它们可能会有一些好处。所以我可以将这些陈述转化为一个简单的设计:

    public interface Drawable 
    {
      public void draw();
      public Position getPos();
    }
    
    public class Rectangle implements Drawable ...
    public class Line implements Drawable ...
    

    现在,State 和 Transition 可以由这些 Drawable 来表示。你听说过单一职责原则吗?这基本上就是它听起来的样子,一个对象应该负责做一件“事情”。矩形和线知道如何绘制自己。状态和转换可能在您的系统中还有其他工作要做。

    public interface Figure 
    {
       public Drawable getDrawable();
    }
    
    public class State implements Figure
    {
       private Rectangle rect;
    
       public Drawable getDrawable() { return rect; }
    
       //... State's real "work" below
    }
    
    public class Transition implements Figure
    {
       private Line line;
       // you get the idea
    }
    

    在小型/简单系统上,SRP 的优势可能会丢失,但我们的想法是我们将渲染与其他系统逻辑分开。我们将功能分离得越多,当需要更改时,系统的脆弱性就越小。

    【讨论】:

    • 这种 SRP 方法对我来说很有意义!
    猜你喜欢
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2015-03-07
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    相关资源
    最近更新 更多