Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java最佳实践:子类中更详细的类变量_Java_Graph_Drawable - Fatal编程技术网

Java最佳实践:子类中更详细的类变量

Java最佳实践:子类中更详细的类变量,java,graph,drawable,Java,Graph,Drawable,我正在用java为一些算法建模一个可绘制的平面图。 我的基本课程是: public class Node { private String label; } 及 这对于算法来说非常有效。为了绘制图形,我使用位置扩展了节点类: public class GraphicalNode extends Node { private int x; private int y; } 我的问题是关于可拉伸边的课程。我想这样写: public class GraphicalEdge

我正在用java为一些算法建模一个可绘制的平面图。 我的基本课程是:

public class Node {
    private String label;
}

这对于算法来说非常有效。为了绘制图形,我使用位置扩展了节点类:

public class GraphicalNode extends Node {
    private int x;
    private int y;
}
我的问题是关于可拉伸边的课程。我想这样写:

public class GraphicalEdge extends Edge {
    private GraphicalNode node0;
    private GraphicalNode node1;
}
public class Node implements Point {
    private String label;
    // ...
}
Edge edge = new Edge();
// initialization here ...
PointsDrawer pd = new PointsDrawer();
pd.drawPoints(edge.getNodes());
但我以前从未见过这样的设计。除此之外,编译器还需要一个构造函数
publicgraphicalnode(node0,node1)
,用于超类


有人知道吗?

您可以使用泛型并用Node类参数化Edge

public class Edge<N extends Node> {
    private N node0;
    private N node1;
    public Edge(N n0, N n1) { this.node0 = n0; this.node1 = n1; }
}
公共类边缘{
私有N节点0;
专用N节点1;
公共边(nn0,nn1){this.node0=n0;this.node1=n1;}
}
然后,GraphicalEdge变为

public class GraphicalEdge extends Edge<GraphicalNode> {
    public GraphicalEdge (GraphicalNode n0, GraphicalNode n1) { super(n0, n1); }
}
公共类GraphicalEdge扩展边缘{
公共GraphicalEdge(GraphicalNode n0,GraphicalNode n1){super(n0,n1);}
}
注意:这些类型的类层次结构通常很难处理。
您也可以简单地决定将
x
y
成员放入to节点基类中,而不需要层次结构。

我认为最简单的方法是让
节点
类实现

public interface Point {
    double getX();
    double getY();
}
像这样:

public class GraphicalEdge extends Edge {
    private GraphicalNode node0;
    private GraphicalNode node1;
}
public class Node implements Point {
    private String label;
    // ...
}
Edge edge = new Edge();
// initialization here ...
PointsDrawer pd = new PointsDrawer();
pd.drawPoints(edge.getNodes());
然后您可以拥有一个类似于原始类的
Edge
类:

public class Edge {
    private Node node0;
    private Node node1;

    public List<Node> getNodes() {
    // ...
}
使用此选项,可以将图形逻辑从数据结构中分离出来,只需在
节点
类中保留x和y坐标即可。您可以通过使用装饰器或
地图存储附加的
数据来进一步解耦它

也许我误解了这里的某些内容-下面的投票将显示-但是

这听起来像是一个教科书上使用的例子

当您有一个类
Edge
具有一个方法
Node getNode()
,那么您可以在一个
扩展Edge
的类中为该方法定义一个更具体的返回类型。例如:

class Node {}
class Edge {
    Node getNode();
}

class GraphicalNode extends Node {}
class GraphicalEdge extends Edge {
    // This really overrides the method, with a more specific return type!
    @Override
    GraphicalNode getNode();
}
或者,使用您提供的类,使用您提到的构造函数和一些getter进行扩展,组装成:


这里的关键点是:当您有一个
Edge
类型的引用时,您只能从中获取一个
节点
(即使引用实际引用的对象是一个
图形边缘
)。仅当引用类型为
GraphicalEdge
时,您还可以从中获取
GraphicalNode

这在许多上下文中都很方便,并且通常允许清晰地分离关注点:当方法只需对
节点
对象进行操作,并且对它们的图形表示不感兴趣时,可以使用基类编写其签名:

void computeSomething(Edge edge) {
    Node n0 = edge.getNode();
    Node n1 = edge.getNode();
    ...
}

void run() {
    GraphicialEdge e = new GraphicalEdge(...);

    computeSomething(e);
}
当一个方法确实需要图形表示时,可以让它使用图形边缘:

void drawSomething(GraphicalEdge edge) {
    GraphicalNode n0 = edge.getNode();
    GraphicalNode n1 = edge.getNode();
    ...
}

void run() {
    GraphicialEdge e = new GraphicalEdge(...);

    computeSomething(e); // Works
    drawSomething(e); // Works as well

    Edge edge = e;
    drawSomething(edge); // Does not work. A GraphicalEdge is required.
}

考虑到你的问题特别是关于

类变量: 根据您在此处绘制的当前设计,每个
GraphicalEdge
将存储其节点两次——一次作为
GraphicalNode
,一次作为简单的
节点。例如,可以通过将事物定义为接口来避免这种情况:

interface Node {}
interface Edge { 
    Node getNode();
}

interface GraphicalNode extends Node {}
interface GraphicalEdge extends Edge { 
    @Override
    GraphicalNode getNode();
}

class DefaultEdge implements GraphicalEdge { ... }


例如,使用泛型可能会增加更多的自由度,考虑到进一步的
节点
类型,可能会有更干净、更灵活的设计。例如,您以后可能希望引入类似于
ColoredGraphicalNode
的内容,泛型类型参数可以很好地涵盖这一点。但它的代价是更为神秘的方法签名:以泛型形式编写方法,允许“路由”所需的类型信息可能会变得有点麻烦,这取决于您要走多远。

eh?我真的听不懂你在说什么。因为你的
图形节点
是一个
节点
,您可以将其存储在
Edge
的成员中,并向
GraphicalEdge
添加两个getter,这两个getter在访问
GraphicalNode
@AndreasFester时封装了强制转换,但是如果在GraphicalEdge中存储一个真实节点而不是一个GraphicalNode,我会冒ClassCastException的风险,不是吗?是的,这就是缺点-您需要确保存储正确的对象。您可以通过@wero使用基于泛型的方法-在那里,编译器确保您只将
GraphicalNodes
存储到
GraphicalEdges
中。在任何情况下:避免子类中的成员重复。谢谢。我认为在这种情况下,weros方法是最明智的。谢谢你的想法。这看起来很有趣。我考虑这个解决方案。我不想把x和y放在节点上,因为如果我只使用图的模型而不使用图的视图,我就不需要它们了。谢谢。但这与在节点中存储x和y坐标是相同的问题。如果我只想使用一个图的模型,那么坐标会包含很多信息。地图也是一个可能的解决方案。我只想使用尽可能少的数据结构。作为一个类变量,我不需要地图。你可以像这样把坐标放在
map
中:
map
我在另一块板上得到了部分相同的答案,我问了一个问题:D我没有得到协方差的方法。这是我最喜欢的!非常感谢你!