0

我遇到了一种情况,我需要更改我的 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);

虽然向后兼容,但有一个错误,因为任何地方都没有提到源代码(无论是在构造函数中还是在函数调用中)。

请在这种情况下建议最佳 API 实践。谢谢

4

3 回答 3

0

您可以使用访问者设计模式,因此您可以将图的遍历与处理节点分开。

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 类的情况下添加更多的访问者,因此这遵循了开放-封闭的设计原则

于 2013-08-05T20:59:48.800 回答
0

您可以以延迟产生错误为代价来保持向后兼容性:

在 v1 中,调用将是编译时错误,runDFS()但在具有向后兼容性的 v2 中,直到运行时调用runDFS()并确定source构造函数未提供时才会出现错误。

用于新方法,如果source构造函数没有提供,它将失败。

没有 asource在构造函数中,只有调用才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);  
于 2013-08-05T21:02:55.860 回答
0

这取决于做什么DFS以及使用或不使用source. 一般来说,只有在没有其他选择或者它已经发展到无论如何都需要重新设计的地步时,你才应该破坏你的 API。

在这种情况下,更改似乎不是主要更改(至少这是我从您的描述中得到的),因此您可以尝试同时满足新旧客户端代码库(参见下面的示例)。

如果您认为合适,您甚至可以将旧构造函数和旧版本标记为runDFSasdeprecated并在将来逐步删除它们。

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      
    }

}
于 2013-08-05T21:37:10.887 回答