5

I have been using delegation pattern to wrap an object created by a factory in a 3rd party library. Recently, the library added a protected method in the base class and my wrapper class doesn't work any longer. Does anyone have a good solution without resorting to reflection?

This is in 3rd party library and in their package,

public class Base {
    public void foo();

    protected void bar(); // Newly added
}

This is in my own package,

public class MyWrapper extends Base {
    private Base delegate;

    public MyWrapper(Base delegate) {
        this.delegate = delegate;
    }

    public void foo() {
        delegate.foo()
    }

    protected void bar() {
        // Don't know what to do
    }
}

EDIT: My original post wasn't clear. These 2 classes are in different packages.

To answer the question why I need delegation. This is a typical use-case of Delegation/Wrapper pattern and I can't show it here in a few lines of code. The library exposes Base class but the actual object from their factory is a derived class of Base. The actual class changes depending on configuration. So I don't know what delegate is. Therefore straight inheritance pattern doesn't work here.

Guillaume Husta
  • 4,049
  • 33
  • 40
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • Well, what is the purpose of that method? Can you just ignore it? I guess not, as you state "doesn't work any longer" ... can you call super.bar()? Just a shot into the blue ... DO you have any whatsoever docu on that new method? – Fildor Apr 26 '13 at 11:37
  • did you tried to move your wrapper class to a package with the same name/path than the one that the base class has?? it's more a hack than a solution but `protected` methods should be accessible within the same package – Alonso Dominguez Apr 26 '13 at 11:38
  • 1
    Base class' `foo()` and `bar()` methods look as if `Base` were an interface or an abstract class. Could you please specify your question? In what way would you need reflection? As it is protected, you may not need to override it at all. – András Hummer Apr 26 '13 at 11:39
  • @Fildor he wants to do `delegate.bar()`, not `this.bar()` – Kent Apr 26 '13 at 11:46
  • Who is supposed to call `bar`? I assume you won't extend `MyWrapper`, so why do you need `bar` in `MyWrapper` in the first place? – Vincent van der Weele Apr 26 '13 at 11:48
  • @Kent, you are right. At second thought, I agree he doesn't need to delegate it at all. – Fildor Apr 26 '13 at 11:54
  • @Fildor I feel the same. – Kent Apr 26 '13 at 12:00
  • Dude, you should be able to use your method as usual. Extending class can use all protected fields and properties of parent class. I just tested your code without any problem. – Ravi Trivedi Apr 26 '13 at 12:03
  • I think we miss the Problem. He is *able* to call `delegate.bar()`, yes. But should he? Obviously the implementation that ignores `bar()` "does not work" ... @ZZCoder: We need more info. – Fildor Apr 26 '13 at 12:10
  • I got his problem. his base class does not have public modifier. Means the class itself is not visible outside package. It has nothing to do with base's protected method. Check out my answer. – Ravi Trivedi Apr 26 '13 at 12:13
  • @Fildor Just updated my question with answers to your question. – ZZ Coder Apr 26 '13 at 13:14
  • OK, so your delegate runtime type is actually a type that extends Base... hmmm ... tricky one. – Fildor Apr 26 '13 at 13:20

5 Answers5

4
Access Levels
Modifier    Class   Package Subclass    World
public      Y          Y        Y         Y
protected   Y          Y        Y         N
no modifier Y          Y        N         N
private     Y          N        N         N

protected has package access too, do you see any specific issue with this:

class Base {
        public void foo(){};

        protected void bar(){}; // Newly added
    }

    class MyWrapper  {
        private Base delegate;

        public MyWrapper(Base delegate) {
            this.delegate = delegate;
        }

        public void foo() {
            delegate.foo();
        }

        protected void bar() {
            // Don't know what to do
            delegate.bar(); //since its in same package, it can be referenced, do you expect compile time error?
        }
    }

Further while using delegator pattern why wrapper class extends Base class, I don't see specific need since you already have an instance of Base. To me it seems more of an decorator.

harsh
  • 7,502
  • 3
  • 31
  • 32
  • The Base class is in a 3rd party library. My class is in a different package. – ZZ Coder Apr 26 '13 at 12:51
  • @ZZCoder you can create the same package structure in your own library and then your wrapper class would be in the _same_ package as the `Base` class, it's a kind of a hack and it would be misleading to the client code of your wrapper, but at least it would give you access to that protected method. That's what I meant in my first comment. – Alonso Dominguez Apr 26 '13 at 13:43
1

You don't need (or shouldn't?) delegate that bar method.

If the library defined a protected method. It doesn't want user (of course in different package) to invoke that method, unless creating subclass of that type. You are trying to break the rule.

If you want to do it, there is way, you create a subtype of Base,say SubBase, you create object of SubBase instead of Base, then pass subBase to your wrapper. then you could in your wrapper bar() method write delegate.bar() In this way, the SubBase is actually the delegate, or to say your wrapper is delegating SubBase not Base

I think you know what I mean, so I just don't type example codes, only this line I guess it is enough:

//Wrapper

 private SubBase delegate;

You see the wrapper is not necessary any longer if you have SubBase. You could even define a public void pubBar() in your SubBase, and there you call this.bar(). in this way all objects of SubBase have access to the protected method (via pubBar()), no matter from which package

Kent
  • 189,393
  • 32
  • 233
  • 301
0

I get the impression that the Base class is either an interface or an abstract class. If so, you must implement the bar() method, for example by using any of the three methods listed below.

Side note, if Base is a normal class, you do not have do anything (a call to an instance of MyWrapper.bar() will actually result in a call to Base.bar().


Delegate to the parent object

This is usually a good idea if the bar() method is used as a callback in the 3rd party API:

@Override
protected void bar() {
    delegate.bar();
}

Do nothing

Just added an empty method to avoid compile errors:

@Override
protected void bar() {
    // silently ignored
}

Throw an exception

Violently prevent any usage of the new method:

@Override
protected void bar() {
    throw new UnsupportedOperationException("not implemented");
}

As a side note, a more robust construction is to prefer composition over inheritance to decouple your implementation from the 3rd party implementation, e.g.

public class MyWrapper {    // does not extend Base
    private Base delegate;

    public MyWrapper(Base delegate) {
        this.delegate = delegate;
    }

    public void foo() {
        delegate.foo()
    }

    // does not have to implement bar()
}
matsev
  • 32,104
  • 16
  • 121
  • 156
  • So you too are saying that he must not delegate the `bar`-Method, correct? In your example he wouldn't have a choice anyway. – Fildor Apr 26 '13 at 13:37
  • @Fildor @ZZCoder See my update. Since the `MyWrapper` class inherits from the `Base` it *must* implement the `bar()` method if `Base` is an abstract class or an interface. – matsev Apr 26 '13 at 15:46
  • Does it have to extend `Base` ? If not, I'd go for matsev's wrapper that avoids inheritance. If yes, then all your clients do not know abut the new method, right? And I guess you are not putting a reference to it into the 3rd party lib ... so in that case I'd just implement an empty "dummy". – Fildor Apr 26 '13 at 15:49
0

I'm using Spring for injecting in Wrapper the "Base delegate" from another module and for getting the delegate i'm using a pojo factory.

<bean id="interfaceDelegate" factory-bean="DelegatePojoFactory"
    factory-method="getMyDelegateInstance">
    <constructor-arg index="0">
        <value>OnLine</value>
    </constructor-arg>
</bean>

<bean id="wrapper" class="path.Wrapper">
      <property name="delegate" ref="interfaceDelegate"></property>
</bean>
Mihai
  • 631
  • 1
  • 14
  • 30
0

If you don't need your delegator to be an actual instance of the base class, you can simply remove that relation:

public class MyWrapper {
    private Base delegate;

    public MyWrapper(Base delegate) {
        this.delegate = delegate;
    }

    public void foo() {
        delegate.foo()
    }

}

Whether that works for you is highly dependent on your other code, and your needs aren't completely clear to me right now.

If the library defines an interface providing the public method which is implemented by the abstract base class, you could sensibly implement that interface without inheriting from the abstract base class, which might also satisfy your needs.

Don Roby
  • 40,677
  • 6
  • 91
  • 113