【问题标题】:How do you preserve interfaces when using the decorator pattern?使用装饰器模式时如何保留接口?
【发布时间】:2011-08-25 08:12:10
【问题描述】:

如果被装饰的对象实现了其他接口的不同组合,你如何在不丢失额外接口方法的情况下实现装饰器?例如,假设我们有以下类和接口,其中 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
}

一旦我们用封装的 I(可能是 C1、C2 或 C3)实例化 D 的实例,我们就无法访问封装的 I 可能实现的 I1、I2、I3 和 I4 的其他方法。

【问题讨论】:

  • 如果D 的实例在概念上不是其中一个接口的实例,我认为您并没有真正“失去”其他方法。

标签: java design-patterns inheritance casting decorator


【解决方案1】:

如果 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);

【讨论】:

    【解决方案2】:

    界面应该是这样的。

    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 或 null 的对象的引用。该对象可以实现其他类型的接口,也可以从其他基类派生。

    如果您想使用其他类型的方法,您可以将对象类型转换为相应的类型并使用。在进行类型转换之前,您可以验证对象是否确实属于某种类型。

    ....
    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# 编写了 sn-p,但对于 Java 也可能保持不变。

    【讨论】:

      【解决方案3】:

      您可以使用 Proxy.newProxy() 作为装饰器。您给它一个要实现的接口列表,您可以通过编程方式包含和处理任意数量的接口,如果您愿意,还可以添加一些您自己的接口。您唯一不能做的就是让代理扩展给定的类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-05
        • 2020-02-29
        • 2017-03-14
        • 2013-05-07
        • 2011-09-05
        • 2014-11-29
        • 1970-01-01
        • 2020-11-06
        相关资源
        最近更新 更多