2015-05-26 12 views
5

Ich habe ein Entwurfsproblem in einem gemeinsamen Dienstprogramm, das wir in unserem Java-Projekt verwenden, um sicherzustellen, dass alle Aufrufer einer bestimmten Methode A von einer anderen Methode B umschlossen werden. die allgemeine Form dieses Codes, wie ich es heute geschrieben habe, ist:Sicherstellen, dass der Aufrufstapel einer Methode immer eine andere Methode in Java enthält

x.B(new Runnable() { 
    y.A(); 
}); 

die runnable dass durch B ausgeführt wird, beliebigen Code haben kann, und rufen kann ein mehrfach, so kann ich nicht loswerden Die Runnable in diesem Code durch Hinzufügen des Aufrufs zu A direkt in B. Auch A ist Code von Drittanbietern, so dass wir es nicht ändern können. Es ist möglich, dass der Runnable B mit einem weiteren verschachtelten Aufruf von A erneut aufrufen könnte, aber heute passiert das nie, also bin ich in Ordnung, diesen Fall für jetzt zu ignorieren.

Ich sehe ein paar Optionen:

  1. A() throws BlahException erklären und machen es so, dass B die einzige Fänger dieser Ausnahme ist. Das ist hässlich, weil es keine Ausnahme gibt, die wirklich geworfen werden sollte, aber es ist nett, weil der Compiler die Aufrufhierarchie für mich sicherstellen würde.
  2. Schreiben Sie eine Art statische Analyse-Tool, um diese Regel für mich zu gewährleisten. Ich habe diesen Fall noch nicht viel untersucht, da es mehr Arbeit als alles andere zu sein scheint (aber vielleicht gibt es ein bereits existierendes Werkzeug, das das kann?).
  3. Fügen Sie dem "Anfang von A" eine Behauptung hinzu (wirklich, dieser Code müsste in einer benutzerdefinierten Version von Runnble leben, da ich A nicht direkt ändern kann), die wir innerhalb eines Anrufs zu B führen. Dies könnte Verwenden Sie entweder einen zusätzlichen Thread-/Objekt-Local-Status oder durchqueren Sie den Call-Stack selbst. Beide sind ziemlich hässlich.

Gibt es andere Optionen, die ich nicht berücksichtigt habe?

+1

Dies ist sehr ein Anti-Muster. Methode 'A' sollte sich nicht darum kümmern, wer sie angerufen hat. Warum denkst du, dass du das tun musst? –

+1

Haben Sie versucht, das Proxy-Muster zu verwenden? – MaxZoom

+0

@KevinKrumwiede - Es ist möglich, dass sowohl "A" von seinem Aufrufer unabhängig ist als auch das Projekt, dass alle Aufrufe von 'A' umschlossen werden sollen. –

Antwort

2

Haben Sie in Betracht gezogen, AspectJ oder einige andere aspektorientierte Programmierwerkzeuge (AOP) zu verwenden? Dann können Sie jeden Aufruf der Methode A abfangen, im StackTrace nach der Methode B suchen. Wenn es nicht vorhanden ist, können Sie die Exception verhindern, die die Ausführung von A verhindert, oder einen Fehler zum Protokollieren schreiben oder tun, was Sie wollen. Etwas wie dieses:

@Aspect 
public class CheckInsideMethodBAspect { 

    @Around("execution(* com.example.AClass.A(..))") 
    public void checkNestedMethod(ProceedingJoinPoint joinPoint) { 

     // checking for method B in the call stack 
     boolean isInsideB = false; 
     StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); 
     for (StackTraceElement element: stackTraceElements) { 
      if (element.getClassName().equals("ClassB") && element.getMethodName().equals("B")) { 
       isInsideB = true; 
       break; 
      } 
     } 

     // if not inside B, throwing exception 
     if (!isInsideB) { 
      throw new NotInsideBException(); 
     } 

     // if inside B, then proceeding with method A execution 
     joinPoint.proceed(); 
    } 

} 
+1

Was auch immer der Grund hinter der Anforderung ist, dass die Methode "B" im Aufruf-Stack ist - d. H. Welche Methode "B" auch immer so wichtig ist - kann wahrscheinlich direkt mit diesem Ansatz erreicht werden, wobei "B" übrig bleibt. –