Tworzenie obiektów klas, które zależą od siebie nawzajem

W systemie istnieją dwie klasy (FooImpl i BarImpl), które zależą od siebie poprzez pole konstruktora.

interface Foo {
  void execute(boolean forward);
}

class FooImpl implements Foo {
  private final Bar bar;

  public FooImpl(Bar bar) {
    this.bar = bar;
  }

  public void execute(boolean forward) {
    System.out.print("Foo");
    if (forward) {
      System.out.print(" -> ");
      bar.execute(!forward);
    }
  }
}

interface Bar {
  void execute(boolean forward);
}

class BarImpl implements Bar {
  private final Foo foo;

  public BarImpl(Foo foo) {
    this.foo = foo;
  }

  public void execute(boolean forward) {
    System.out.print("Bar");
    if (forward) {
      System.out.print(" -> ");
      foo.execute(!forward);
    }
  }
}

Taki stan nie pozwala stworzyć obiektów tychże klas bez konieczności wprowadzenia dodatkowego obiektu pośredniczącego wywołania do poprawnie stworzonego obiektu. W języku Java funkcjonalność tą realizuje klasa java.lang.reflect.Proxy.

Aby stworzyć takie proxy potrzebna będzie jeszcze implementacja interfejsu java.lang.reflect.InvocationHandler, do której przekazane będą wszystkie wywołania metod obiektu proxy.

class DelegateInvocationHandler implements InvocationHandler {
  private Object delegate;

  public void setDelegate(Object delegate) {
    this.delegate = delegate;
  }

  public Object invoke(Object target, Method method, Object[] args)
      throws Throwable {
    return method.invoke(delegate, args);
  }
}

Aby zbudować obiektu obu klas najpierw przygotujemy proxy FooImpl. Korzystając z obiektu fooProxy możemy stworzyć obiekty klas FooImpl i BarImpl za pomocą operatora new. Ostatnim etapem wymaganym do poprawnego działania proxy potrzebne jest ustawienia obiektu delegata. Po wykonaniu tych wszystkich operacji wywołania metod execute zostaną przekazane do odpowiednich obiektów.

DelegateInvocationHandler handler = new DelegateInvocationHandler();

Foo fooProxy = (Foo) Proxy.newProxyInstance(
  Foo.class.getClassLoader(),
  new Class[]{Foo.class},
  handler);

Bar bar = new BarImpl(fooProxy);
Foo foo = new FooImpl(bar);

handler.setDelegate(foo);

foo.execute(true); // na wyjsciu Foo -> Bar
bar.execute(true); // na wyjsciu Bar -> Foo