2013-08-05 108 views
0

我遇到了一個情況,我需要改變我的API,要知道我有最好的選擇需要: 最初我的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,因爲在任何地方都沒有提到這個源碼(在構造函數和函數調用中都沒有提到)。

請在本方案中建議最佳API實踐。謝謝

回答

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  
    } 

} 
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

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);