2013-08-05 3 views
0

나는 내가 가지고있는 가장 좋은 방법을 알 필요가 내 API를 변경하는 데 필요한 상황에 달렸다 : 처음에 내 API는 말했다 : 이제API 설계 변경 모범 사례

DFS dfs = new DFS(Graph); 
dfs.runDFS(source); 

, 나는 또 다른 기능을 추가 내 DFS 코드, 입력 정점에서 소스로 dfs 경로를 반환합니다. 나는 이전 버전과의 호환성을 유지한다면

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 보인다 버그가 있습니다. coz 소스는 어디에도 언급되어 있지 않습니다 (생성자 나 함수 호출 모두에서 언급되지 않음).

이 시나리오에서 최상의 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

디자인 패턴을 사용할 수 있으므로 그래프 처리와 노드 처리를 분리 할 수 ​​있습니다.

// overloads runDFS(GraphNode source) 
public void runDFS(GraphNode source, NodeVisitor visitor) { 
    // do traversing, then 
    visitor.visit(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 클래스는이 인스턴스를 받아 들여야 :

public interface NodeVisitor { 
    public void visit(GraphNode 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)에 대한 호출 만 작동합니다.

또한 서브 클래스에 의해 완전히 새로운 API를 만들 수 2

아이디어 :

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