如何使用受保护的方法为Java类实现装饰器模式

And*_*rey 7 java design-patterns

包外的子类无法访问父类实例上的受保护成员(仅限于子类本身或其子类的实例).JLS链接:http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.2

这是一个例子.现有的类看起来如下:

package package1;
public abstract class BaseImplementation {

    public String getResource1() {
        return processTemplate1(getBaseUrl());
    }

    public String getResource2() {
        return processTemplate2(getBaseUrl());
    }

    // Kind of 'Template Method' pattern.
    protected abstract String getBaseUrl();
}
Run Code Online (Sandbox Code Playgroud)

所以打算写下如下装饰器:

package package2;
public class ImplementationDecorator extends BaseImplementation {
    private BaseImplementation delegate;

    public ImplementationDecorator(BaseImplementation delegate) {
        this.delegate = delegate;
    }

    @Override
    protected String getBaseUrl() {
        return trackServer + "?redirect=" + delegate.getBaseUrl();
    }
}
Run Code Online (Sandbox Code Playgroud)

代码不会编译. getBaseUrl()在基类中具有受保护的访问权限,甚至子类也无法访问父实例上的访问权限.

所以问题是如何使用受保护的方法来装饰这样的实例,而不使用像反射这样的"脏"技巧或将子类放到与父类同名的包中.

Java语言本身也有相同的例子(例如javax.security.auth.login.ConfigurationSpi),在我发现的Java中 - 使用来自同一个包的访问.

seh*_*seh 3

您在这里尝试做的是拦截在类型与其派生类型之一之间的关系中或多或少私有的行为BaseImplementation- 这里是您想要在类中装饰的“委托”实例ImplementationDecorator。的作者BaseImplementation从未预料到有人想要进入中间,并且派生类型的作者也BaseImplementation没有预料到除BaseImplementation自身之外的任何调用者都会进入那里。

相反,您正在寻找的模式看起来更像是这样,如果您不拥有所涉及的类,恐怕您无法对其进行改造:

public interface URLProvider
{
  String getBase();
}


public final class BaseImplementation
{
  public BaseImplementation(URLProvider provider)
  {
    if (null == provider)
      throw new NullPointerException();
    this.provider = provider;
  }


  public String getResource1()
  {
    return processTemplate1(provider.getBase());
  }


  public String getResource2()
  {
    return processTemplate2(provider.getBase());
  }


  private final URLProvider provider;
}
Run Code Online (Sandbox Code Playgroud)

有了这个,如果有人编写了 type 的实现URLProvider,那么修饰起来就会很容易,因为现在方法URLProvider#getBase()是公共的。

现在,您可能会说,好吧,这不是与将您的BaseImplementation#getBaseUrl()方法更改为公开而不是受保护一样吗?不完全的。在这里,设计承认——甚至警告——实例URLProvider是一种可以在任何地方使用的功能。