我遇到了需要更改API的情况,需要知道我的最佳选择: 最初我的API声明:
DFS dfs = new DFS(Graph);
dfs.runDFS(source);
现在,我在我的DFS代码中添加了另一个函数,将dfs路径从输入顶点返回到源。 因此,我的新干净API看起来像:
DFS dfs = new DFS(Graph, source); // BREAKS THE CONTRACT.
dfs.runDFS(); // BREAKS THE CONTRACT.
dfs.getPathFromSource(vertex);
如果我确实保持向后兼容性(维护2个构造函数和2个runDFS函数),我的客户端会遇到另一个问题:
DFS dfs = new DFS(Graph);
dfs.runDFS();
dfs.getPathFromSource(vertex);
虽然向后兼容,但有一个bug,coz源在任何地方都没有提到(无论是在构造函数中还是在函数调用中)。
请在此方案中建议最佳API实践。感谢
答案 0 :(得分:0)
您可以使用visitor设计模式,这样您就可以将图表的遍历与处理节点分开。
public interface NodeVisitor {
public void visit(GraphNode node);
}
您需要一个可以进行路径计算的实现:
public class PathCalculatingVisitor implements NodeVisitor {
public PathCalculatingVisitor(GraphNode target) {
// source will be the start of the traversal
}
public void visit(GraphNode node) {
// implement path calculation logic
}
}
您的DFS类应接受以下实例:
// overloads runDFS(GraphNode source)
public void runDFS(GraphNode source, NodeVisitor visitor) {
// do traversing, then
visitor.visit(node);
}
最后,用法:
PathCalculatingVisitor pathCalculatingVisitor = new PathCalculatingVisitor(target);
DFS dfs = new DFS(graph);
dfs.runDFS(source, pathCalculatingVisitor);
pathCalculatingVisitor.getPath();
我认为此设计更具未来性,如果需要,您可以在不触及DFS课程的情况下添加更多访问者,因此这符合open-closed design principle。
答案 1 :(得分:0)
您可以以延迟产生错误为代价来保持向后兼容性:
在v1中,调用runDFS()
将是一个编译时错误,但在v2 w /向后兼容性中,在调用runDFS()
和运行时之前,您不会收到错误它确定构造函数未提供source
。
对于新方法,如果构造函数没有提供source
,则失败。
在构造函数中没有source
,只有对runDFS(source)
的调用有效。
创意2
您还可以通过子类化创建一个全新的API:
public class DFS2 extends DFS {
private Object mySource;
public DFS2(Graph g) {
super(g);
}
public DFS2(Graph g, Object source) {
mySource = source;
}
public void runDFS() {
super.runDFS(mySource);
}
public Path getPathFromSource(Vertex vertex) {
.... code goes here ...
}
}
DFS2获得新的API。旧代码仍然使用旧代码,您可以共享大部分代码。
DFS2 dfs = new DFS2(Graph, source);
dfs.runDFS();
dfs.getPathFromSource(vertex);
答案 2 :(得分:0)
这取决于DFS
的作用以及使用或不使用source
进行操作是否有意义。一般来说,只有在没有其他选项或者它已经发展到需要重新设计的程度时,你才应该破坏你的API。
在这种情况下,更改似乎不是主要的(至少这是我从您的描述中获得的),因此您可以尝试立即满足旧的和新的客户端代码库(请参阅下面的示例)。
如果您认为合适,您甚至可以将旧构造函数和旧版runDFS
标记为deprecated
,并在将来逐步删除它们。
public class DFS {
public DFS(Graph graph) { ... } // you have the option to mark this as deprecated
public DFS(Graph graph, Source source) { ... } // New constructor
public void runDFS() { // New API
if (this.source == null) {
throw new IllegalStateException("Source is null!");
}
doRun(this.source);
}
// Again you have the option to mark this as deprecated
public void runDFS(Source source) {
// handle here the case where client already provided a source with the new
// constructor. Should we replace it? Should we throw an exception?
this.source = source;
doRun(source);
}
private void doRun(Source source) {
// this is private so it can be called by both runDFS() and runDFS(Source)
// do whatever you did before here
}
public Path getPathFromSource(Vertex vertex) { // New API
if (source == null) {
throw new IllegalStateException("Source is null!");
}
// do the job for the new API here
}
}