我不太确定在面向对象的世界中建模这个问题的最佳方法是什么。
假设我必须用Java表示图形及其节点,并假设我想使用深度优先搜索(DFS)遍历图形。
这两者中软件工程的最佳方法是什么:
创建一个类Node
,一个类Graph
和一个类GraphsFunctions
,其中GraphsFunctions
类包含DFS
方法,该方法采用{ {1}}和Graph
作为参数。这样的事情:Node
和电话public void DFS(Graph g, Node n)
创建一个类new GraphsFunctions().DFS(new Graph(), new Node())
和一个类Node
,其中类Graph
包含仅Graph
作为参数的方法DFS
。这样的事情:Node
和电话public void DFS(Node n)
我宁愿使用这两个选项中的第一个,但我不能说为什么它对我来说是最好的。你能告诉我什么是最好的选择,实际上是最好的吗?
答案 0 :(得分:5)
这主要取决于两件事:
假设您希望(仅)良好封装,我个人会像下面这样做..
创建一个界面GraphSearch
并在其中定义您的search(Graph g, Node n)
方法。现在有一个名为DFSearch
和可能BFSearch
的班级。
如果 - 在某个时刻 - 某个方法想要在Graph
上执行搜索,您可以指定应该使用的搜索算法。
如果您想要精彩封装,我建议您使用iterator pattern作为DFS
,而BFS
基本上只是迭代订单。
首先创建一个接口GraphIterator
。它只是扩展Iterator<Node>
,这意味着它可以按某种顺序迭代节点。
public interface GraphIterator extends Iterator<Node> {
}
根据需要创建此算法的任意数量的实现。 DFS
的空结构如下所示:
public class DFSIterator implements GraphIterator {
private Graph g;
public DFSIterator(Graph g) {
this.g = g;
}
@Override
public boolean hasNext() {
// todo: implement
return false;
}
@Override
public Node next() {
// todo: implement
return null;
}
}
接下来,您仍然可以创建界面GraphSearch
..
public interface GraphSearch {
Node search(Graph g, Node n);
}
一个简单的实现是IterationSearch
,它只能执行给定Iterator
的搜索。
public class IterationSearch {
public Node search(Graph g, Node n, GraphIterator iter){
Node current = null;
while (iter.hasNext()){
current = iter.next();
if (current.equals(n)){
return n;
}
}
return null;
}
}
它只是迭代Iterator
并将每个元素与搜索到的节点进行比较。接下来创建您的班级DFSearch
,这是第一个实际的GraphSearch
...
public class DFSearch extends IterationSearch implements GraphSearch{
@Override
public Node search(Graph g, Node n) {
return search(g, n, new DFSIterator(g));
}
}
现在你的Graph
可以扩展Itarable<Node>
并返回一个迭代器作为默认迭代器。通过这种方式,您可以很好地迭代Graph
。
public class Graph implements Iterable<Node> {
@Override
public Iterator<Node> iterator() {
return new DFSIterator(this);
}
}
并使用它如下:
Graph g = createGraph();
for (Node n : g) {
// do things...
}
此解决方案的优点在于,您可以创建Iterator
的{{1}}内部类,这允许他们访问私有成员。这通常可以显着提高效果,因为您不必将Graph
视为black box。
答案 1 :(得分:1)
在快速编码,易于修改或其他一些标准方面最好?除非你想出一个非常精确的“什么是好”的定义,否则不要期望找到“最好的”。
我的第一个答案是使用Java图形库。大多数人已经实施了DFS:
但是,由于你的问题可能是自己动手课程的一部分,我建议:
Node
类,可以提供对邻居的访问,并且具有equals和hashcode方法。Graphs
类,可以构建图形并显示它们。包含Dfs(Graph g, Node start, DfsVisitor visitor)
方法。这将是一个具有全静态方法的实用程序类,与JDK的Collections
或Files
完全相同。 Graph
类,包含节点列表(以及可能的其他内容,例如将迭代器返回到边列表的可能性)。 DfsVisitor
,访问者界面:Main
类,它构建一个图形并使用访问者中有用的有效负载调用图表上的DFS。因为DFS 本身是无用的 - 它只是一个节点访问策略。它的值在之前和之后访问节点时的值。除非您允许自定义,否则将DFS算法放入GraphsFunctions
或Graphs
没有太大区别:它不可能在您构建到其中的任何有效负载之外重用。
public interface DfsVisitor() {
void started(Graph g, Node node); // when first encountered
void finished(Graph g, Node node); // when all children processed
}