API设计改变了最佳实践

时间:2013-08-05 20:52:14

标签: java api design-patterns

我遇到了需要更改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实践。感谢

3 个答案:

答案 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      
    }

}