6

I want to Decorate (Decorator design pattern) a common base class, but the method I need to Decorate is protected. See example:

public class AbstractActor {
   public void act(){...}      //Delegates its actions to doAct() based on some internal logic
   protected void doAct(){...}
}

Subclasses are meant to override doAct(), I need to inject some functionality there. I can override doAct, but my decorator class can't access the protected method doAct() on the instance being decorated. Example:

public class ActorDecorator extends AbstractActor {
   AbstractActor decoratedInstance;
   public ActorDecorator(AbstractActor decoratedInstance){
      this.decoratedInstance = decoratedInstance;
   }
   protected void doAct(){
      //Inject my code
      decoratedInstance.doAct();    //Can't call protected method of decoratedInstance
   }
   //Other decorator methods
}

Is there some solution for this challenge?

p27
  • 2,217
  • 1
  • 27
  • 55
David Parks
  • 30,789
  • 47
  • 185
  • 328

4 Answers4

8

If you put AbstractActor and ActorDecorator in the same package, you will be able to access the protected method.

darioo
  • 46,442
  • 10
  • 75
  • 103
0

how about you extend the class you are trying to decorate and provide a public function that calls the protected function (to which you will have access to) and then decorate the extended class? so in your example:

public class ToDecorateActor extends AbstractActor {
 public void publicAct() {
  doAct();
 }
}

public class ActorDecorator {
   ToDecorateActor decoratedInstance;
   public ActorDecorator(ToDecorateActor decoratedInstance){
      this.decoratedInstance = decoratedInstance;
   }
   protected void doAct(){
      //Inject my code
      decoratedInstance.publicAct();
   }
   //Other decorator methods
}
Liv
  • 6,006
  • 1
  • 22
  • 29
  • The issue here is that I have instances of AbstractActor (and it's multitude of subclasses) I need to decorate, not instance of ToDecorateActor. If I'm given, for example, hollywoodActor (HollywoodActor extends AbstractActor) I cannot call new ActorDecorator(hollywoodActor). – David Parks Apr 21 '11 at 10:46
  • I don't like this answer. basically you are changing the protected method to a public one. – Zoltan Balazs Apr 21 '11 at 10:50
  • When a class designer keeps a method protected then it is supposed to remain that way! If you violate encapsulation this way then that is setting very bad programming example for everyone and more trouble for consumers of class. – Nilesh Apr 21 '11 at 11:50
  • It would be fine to convert the method to public for my own use because, as a Decorator, I am actually a subclass, and morally right (if you will) in using the protected method. But this approach doesn't allow me to decorate the subclasses of AbstractActor, my original intention. – David Parks Apr 22 '11 at 04:08
  • you can provide a constructor for ToDecorateActor that wraps (once more) any instances of ActorDecorator -- though I agree that's 2 decorations involved... – Liv Apr 22 '11 at 11:59
0

I think using the same package name is the best answer, but I had one more that popped into my own mind that I'll comment on.

It should be possible to use Aspect Oriented Programming (AOP, AspectJ for example) to inject advice around the doAct() method. As I am working in a Spring environment and have AOP programming available I may look into using this functionality.

I've added this answer as a bit of food for thought, I've yet to use AOP to deal with this problem.

David Parks
  • 30,789
  • 47
  • 185
  • 328
-2

When a class designer keeps a method protected then it is supposed to remain that way! If you violate encapsulation this way then that is setting very bad programming example for everyone and more trouble for consumers of class.

Having said that, in your case, what stops you from injecting your code in the overriden act() call? Which anyways does call your protected method doAct()?

Hope that helps.

Nilesh
  • 4,137
  • 6
  • 39
  • 53
  • act() has quite a bit of logic in it which may or may not call doAct() depending on conditions. I only want to inject functionality when doAct() is invoked. In this case I would need to duplicate the logic of act(), which would be a gross violation of good programming doctrine. The fact that it's protected is so subclasses can override it. In this case I am a subclass, the use of the Decorator pattern is employed to avoid subclassing a large number of AbstractActor subclasses. The creators of this library created doAct() with my intention in mind, but did not lend it to be Decorated. – David Parks Apr 22 '11 at 04:13
  • "When a class designer keeps a method protected then it is supposed to remain that way!" That's not true. When you have your classes in a Jar file like when you are using a framework, the only way to change behavior is through extension, but there are case where you need runtime extension which requires composition, and for that you either need public methods or locate your classes in the same package. – CCC May 06 '12 at 19:53