Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement decorator pattern for Java class with protected methods

Subclass outside the package cannot access protected members on instances of parent class (only on instances of subclass itself or its subclasses). JLS link: http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.2

Here is an example. There is an existing class looking as the following:

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

So intention is to write decorator like the following:

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

The code will not compile. getBaseUrl() has protected access in base class, and even subclasses cannot access that on parent instances.

So question is how to decorate such instances with protected methods without using 'dirty' tricks like reflection or putting subclass to package with the same name as parent class.

There are also examples of the same in Java language itself (e.g. javax.security.auth.login.ConfigurationSpi), and in cases I've found in Java - access from the same package is used.

like image 370
Andrey Avatar asked Jul 28 '11 17:07

Andrey


1 Answers

What you're trying to do here is intercept behavior that's more or less private in the relationship between type BaseImplementation and one of its derived types—here, your "delegate" instance that you'd like to decorate in your ImplementationDecorator class. The author of BaseImplementation never anticipated someone wanting to get in the middle there, and the author of the type derived from BaseImplementation didn't anticipate that any callers other than BaseImplementation itself would get in there.

Instead, the pattern you're looking for would look more like this, which I'm afraid you can't retrofit if you don't own the classes involved:

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

With that in place, if someone had written an implementation of type URLProvider, it would be easy to decorate, because now method URLProvider#getBase() is public.

Now, you may say, well, isn't that the same as just changing your BaseImplementation#getBaseUrl() method to be public, rather than protected? Not quite. Here, the design acknowledges—or even warns—that the URLProvider instance is a capability that can be used from anywhere.

like image 181
seh Avatar answered Nov 15 '22 00:11

seh