2013-02-19 2 views
2

내 클래스에 자원을 확보하는 start() 메소드와 자원을 해제하는 stop() 메소드가 있다고 가정합니다. 클래스의 start 메소드는 멤버 객체의 start() 메소드를 호출 할 수 있습니다. 멤버 객체 중 하나의 start()가 예외를 throw하는 경우 start()가 성공한 모든 멤버 객체에 대해 stop()이 호출되는지 확인해야합니다.Java에서 unwind-like 정리 코드 작성

class X { 
    public X() 
    { 
     a = new A(); 
     b = new B(); 
     c = new C(); 
     d = new D(); 
    } 

    public void start() throws Exception 
    { 
     try { 
      a.start(); 
     } catch (Exception e) { 
      throw e; 
     } 

     try { 
      b.start(); 
     } catch (Exception e) { 
      a.stop(); 
      throw e; 
     } 

     try { 
      c.start(); 
     } catch (Exception e) { 
      b.stop(); 
      a.stop(); 
      throw e; 
     } 

     try { 
      d.start(); 
     } catch (Exception e) { 
      c.stop(); 
      b.stop(); 
      a.stop(); 
      throw e; 
     } 
    } 

    public void stop() 
    { 
     d.stop(); 
     c.stop(); 
     b.stop(); 
     a.stop(); 
    } 

    private A a; 
    private B b; 
    private C c; 
    private D d; 
} 

클린업 코드의 2 차 증가에주의하십시오. 정리를 수행하는 가장 좋은 방법은 무엇입니까 (최소 코드 금액)? C에서는 함수의 맨 아래에있는 정리 코드를 사용하여 쉽게이 작업을 수행 할 수 있으며 "goto"를 사용하여 적절한 위치로 이동할 수 있습니다.하지만 Java에는 goto가 없습니다. start()되지 않은 객체에서 stop()을 호출하는 것은 허용되지 않습니다. 위와 동일하지만 더 짧은 코드를 찾고 있습니다.

지금까지 내가 왔어요 유일한 해결책은 다음과 같이 시작 있었는지 기억하는 논리 값을 사용하는 것입니다, 나는 "마지막으로"에 대해 알고

public void start() throws Exception 
{ 
    boolean aStarted = false; 
    boolean bStarted = false; 
    boolean cStarted = false; 
    boolean dStarted = false; 

    try { 
     a.start(); 
     aStarted = true; 
     b.start(); 
     bStarted = true; 
     c.start(); 
     cStarted = true; 
     d.start(); 
     dStarted = true; 
    } catch (Exception e) { 
     if (dStarted) d.stop(); 
     if (cStarted) c.stop(); 
     if (bStarted) b.stop(); 
     if (aStarted) a.stop(); 
     throw e; 
    } 
} 

을하고 "시도 --자원"하지만, 예외가 없으면 리소스를 해제해서는 안되기 때문에 여기에 해당되지 않는 것으로 보입니다.

P. 이것은 예외 사용이나 프로그램 설계에 대한 질문이 아닙니다. 특히 초기화 코드가 실패 할 경우 정리하는 것입니다.

+0

시작되지 않은 리소스에 'stop'을 호출하면 어떻게됩니까? – assylias

+0

"시작되지 않은 객체에서 stop()을 호출 할 수 없다는 것을 언급했습니다." 이를 위해 start() 및 stop() 함수에 assert()가있을 수 있습니다. –

+2

stop() 메소드를보다 강력하게 만들고 항상 호출하십시오. –

답변

1

주어진 모든 아이디어를 고맙게 생각하지만, 널리 사용되는 코드는 내 코드에 적합하지 않습니다. 특히, 스택 /리스트 기반 접근법은 다음과 같은 두 가지 이유로 인해 문제가된다.

  1. start() 래퍼는 호출하는 객체의 시작 메소드에 인수를 전달할 수 없다.
  2. 모든 것은 정지 가능한 인터페이스를 구현해야합니다. 이것은 기술이 외부에서 제공되는 클래스와 함수에서 작동해야하기 때문에 문제가 있습니다. start() 메소드가 없어도 다른 것일 수도 있습니다. 인터페이스는 프로그래머의 통제 될 수있다 -

생각는 시작되지 객체가 같은 이유로 적합하지 않은 경우에도 호출) (정지를 확인합니다.

결국이 문제를 해결하기 위해 최소의 상용구가 필요합니다. 또 다른 이점은 객체가 시작되지 않은 경우에도 결과 stop() 메서드를 실제로 호출 할 수 있다는 것입니다. 그러나 멤버의 시작 및 중지 기능이 프로그래머가 제어 할 수 없으므로 접근 방법이 무의미합니다.

class X { 
    public X() 
    { 
     a = new A(); 
     b = new B(); 
     c = new C(); 
     d = new D(); 
    } 

    public void start() throws Exception 
    { 
     assert(state == 0); 
     try { 
      a.start(); 
      state = 1; 
      b.start(); 
      state = 2; 
      c.start(); 
      state = 3; 
      d.start(); 
      state = 4; 
     } catch (Exception e) { 
      stop(); 
      throw e; 
     } 
    } 

    public void stop() 
    { 
     if (state >= 4) d.stop(); 
     if (state >= 3) c.stop(); 
     if (state >= 2) b.stop(); 
     if (state >= 1) a.stop(); 
     state = 0; 
    } 

    private int state; 
    private A a; 
    private B b; 
    private C c; 
    private D d; 
} 
+0

이제 질문은 : ** 변수 값을 읽는 것이 안전합니까? catch 블록의 상태입니까? ** C에서, 예외 처리는 setjmp/longjmp에 의해 구현되며 try 블록 내에서 수정 된 경우 로컬 변수가 volatile이어야합니다. –

4

시작한 항목을 스택에 추가 한 다음 물건을 중지해야하는 경우 스택에서 모든 것을 팝하고 중지하십시오.

private Deque<Stoppable> toStop = new ArrayDeque<Stoppable>(); 

public void start() throws Exception { 
    try { 
    start(a); 
    start(b); 
    start(c); 
    start(d); 
    } catch (Exception e) { 
    stop(); 
    throw e; 
    } 
} 

private void start(Stoppable s) throws Exception { 
    s.start(); 
    toStop.push(s); 
} 

public void stop() { 
    while (toStop.size > 0) { 
    toStop().pop().stop(); 
    } 
} 

는 인터페이스를 통해 또는 서브 클래스에 의해 하나 일반적인 stop()의 일종을 가지고 시작 물건을 필요로하지만 나는 그들이 이미 수행의 가능성을 상상한다.

public void start() throws Exception 
{ 
    a.start(); 
    try { 
     b.start(); 
     try { 
      c.start(); 
      try { 
       d.start(); 
      } catch (Exception e) { 
       c.stop(); 
       throw e; 
      } 
     } catch (Exception e) { 
      b.stop(); 
      throw e; 
     } 
    } catch (Exception e) { 
     a.stop(); 
     throw e; 
    } 
} 

당신이 정말로 몇 가지 항목 이상이있는 경우/정지, 사용을 시작 : 당신이 코드의 선형 폭발과 함께 확인하는 경우

+0

모두 중지 된 후에'toStop'을 지우는 것을 잊지 마십시오. – ogzd

+0

@ogzd - 좋은 점 – Qwerky

+0

a.start() 메서드는 start() 래퍼를 통해 전달되어야하는 인수를 사용하기 때문에이 코드는 실제로 작성된대로 작동하지 않습니다. 또한 정리를 역순으로 수행해야합니다. –

2
public class X 
{ 
    private final List <Stoppable> stoppables = 
     new ArrayList <Stoppable>(); 

    private void start (StartStoppable x) 
    { 
     x.start(); 
     stoppables.add (x); 
    } 

    public void startAll() 
    { 
     try 
     { 
      start (a); 
      start (b); 
      start (c); 
      start (d); 
     } 
     catch (Throwable ex) 
     { 
      stopAll(); 
      ex.printStackTrace(); 
     } 
    } 

    public void stopAll() 
    { 
     for (Stoppable s: stoppables) 
     { 
      try 
      { 
       s.stop(); 
      } 
      catch (Throwable ex) 
      { 
       ex.printStackTrace(); 
      } 
     } 
    } 
} 
1

, 당신은 다음과 같이 구조화 start 방법을 사용할 수 있습니다 다른 사람이 제안한 것과 마찬가지로 List.

+0

예 중첩을 피하려고했습니다.이 모양이 못 생겼다는 것을 인정해야합니다. :) –