开发者

How do you preserve interfaces when using the decorator pattern?

开发者 https://www.devze.com 2023-03-06 01:29 出处:网络
If the objects being decorated implement different combinations of other interfaces, how do you implement a decorator without losing the extra interface methods?For example, say we have the following

If the objects being decorated implement different combinations of other interfaces, how do you implement a decorator without losing the extra interface methods? For example, say we have the following classes and interfaces, where I is the one we care about for decorating and D is the implementation of a decorator:

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
}

Once we instantiate an instance of D with a wrapped I, which may be C1, C2, or C3, we lo开发者_如何学Cse access to the additional methods for I1, I2, I3, and I4 that the wrapped I may implement.


If C1, C2, C3 would be interfaces there will be a proxy solution.

interface C1 extends I, I1, I2

Else you would need a library like cglib to decorate the class.

A proxy in combination with a generic factory method will preserve the other interfaces so you need no cast in the code:

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
}

Now you can use it the following way:

C1 c1 = ...;
c1 = D.decorate(c1);


The interface would be something like this.

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

class D need not perform wrapping. What it does is implement 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;
  }
}

Object of type I holds a reference to other object of type I or null. This object may implement interfaces of other types or may be derived from other base classes.

If you want to use methods of other types, you can typecast the object to respective type and use. Before typecasting you can verify if the object is indeed of a certain type or not.

....
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
}
....

I have written snippet in C#, but it may remain same for Java as well.


You can use Proxy.newProxy() as the decorator. You give it a list of interfaces to implement and you can programatically include and handle any number of interfaces and add a few of your own if you wish. The only thing you can't do is have the Proxy extend a given class.

0

精彩评论

暂无评论...
验证码 换一张
取 消