Как вы сохраняете интерфейсы при использовании шаблона декоратора? - PullRequest
1 голос
/ 14 мая 2011

Если декорируемые объекты реализуют различные комбинации других интерфейсов, как реализовать декоратор без потери дополнительных методов интерфейса? Например, скажем, у нас есть следующие классы и интерфейсы, где I - это тот, который нам нужен для декорирования, а D - реализация декоратора:

class C1 implements I, I1, I2

class C2 implements I, I2, I3

class C3 implements I, I3, I4

class D implements I {
    I wrappedValue
    // Methods for I
}

Как только мы создаем экземпляр D с упакованным I, который может быть C1, C2 или C3, мы теряем доступ к дополнительным методам для I1, I2, I3 и I4, которые может реализовать упакованный I.

Ответы [ 3 ]

1 голос
/ 14 мая 2011

Если C1, C2, C3 будут интерфейсами, будет решение для прокси.

interface C1 extends I, I1, I2

В противном случае вам потребуется библиотека типа cglib для украшения класса.

Прокси в сочетании с универсальным фабричным методом сохранит другие интерфейсы, поэтому вам не нужно приводить код:

class D<T_I extends I> implements InvocationHandler, I {

  public static <T_I extends I> T_I decorate(T_I wrappedValue) {
    return (T_I)Proxy.newProxyInstance(
        wrappedValue.getClass().getClassLoader(),
        getAllInterfaces(wrappedValue.getClass()),
        new D<T_I>(wrappedValue));
  }

  private static Class[] getAllInterfaces(Class type) {
    if (type.isInterface()) {
      Class[] all = new Class[type.getInterfaces().length + 1];
      int i = 0;
      all[i++] = type;
      for (Class t : type.getInterfaces()) {
        all[i++] = t;
      }
      return all;
    } else {
      return type.getInterfaces();
    }
  }


  private final T_I wrappedValue;

  private D(T_I wrappedValue) {
    this.wrappedValue = wrappedValue;
  }

  public Object invoke(Object proxy, Method method, Object[] args) {
    if (method.getDeclaringClass() == I.class) {
      // call wrapped method in D
      return method.invoke(this, args);
    }
    //call unwrapped method of other interface
    return methos.invoke(wrappedValue, args);
  }

  // Methods for I
}

Теперь вы можете использовать его следующим образом:

C1 c1 = ...;
c1 = D.decorate(c1);
0 голосов
/ 14 мая 2011

Вы можете использовать Proxy.newProxy () в качестве декоратора.Вы предоставляете ему список интерфейсов для реализации, и вы можете программно включать и обрабатывать любое количество интерфейсов и добавлять несколько своих собственных, если хотите.Единственное, что вы не можете сделать, - это чтобы Proxy расширял данный класс.

0 голосов
/ 14 мая 2011

Интерфейс будет примерно таким.

interface I
{
   I objI {get;} // can hold reference to other objects of type I or null
   // If the requirement is to make setter as public, that should be fine as well.

   // You can have decorator related behavior here
   void DecoratorBehavior1(); 
   void DecoratorBehavior2(); 
}

класс D не должен выполнять обтекание. Что он делает, так это реализует I.

class D implements I
{
  public I objI {get; private set;}
  // You can have other overloaded constructors as well
  D(I obj)
  {
     this.objI = obj;
  }
}

Объект типа I содержит ссылку на другой объект типа I или ноль. Этот объект может реализовывать интерфейсы других типов или может быть производным от других базовых классов.

Если вы хотите использовать методы других типов, вы можете типизировать объект к соответствующему типу и использовать. Перед приведением типов вы можете проверить, действительно ли объект относится к определенному типу.

....
I objC1 = new C1(null);
I objC2 = new C2(objC1);
I objC3 = new C3(objC2);
I objD = new D(objC3);
...
I oGetBackC3 = objD.objI;
if(oGetBackC3 is typeof(C3))
{
   C3 oGotBackC3 = (C3)oGetBackC3;
   ...
   // You can now call C3 methods on object
}
....

Я написал фрагмент на C #, но он может остаться таким же и для Java.

...