5

I am implementing template pattern in Java. Let us assume the following piece of code:

public abstract class A {

  public final void work() {
    doPrepare();
    doWork();
  }

  protected abstract void doPrepare();

  protected abstract void doWork();
}

public final class B extends A {

  @Override 
  protected abstract void doPrepare() { /* do stuff here */ }

  @Override 
  protected abstract void doWork() { /* do stuff here */ }
}

public final class Main {

  public static void main(String[] args) {
    B b = new B();
    b.work();
  }
}

I consider it a problem that one is easily able to accidentally call b.doWork() instead of always calling b.work(). What is the most elegant solution, if possible, to "hide" the hooks?

  • "one is easily able to accidentally call b.doWork()". Really? It´s protected, so only A or B instances can call it. – Oskar Hýbl Feb 20 '16 at 21:30
  • Unfortunately, since it's protected, all classes from the package can call it. Splitting packages is a way to go, though I'd rather avoid having 1- or 2-class packages. – Peter Lényi Feb 20 '16 at 21:36
  • No!! You are describing default access modifier (i.e. none). Protected means "only me or classes that extend me can use this" – Oskar Hýbl Feb 20 '16 at 21:38
  • @OskarHýbl No, you're wrong. `protected` means subclasses and all classes from the same package. – fps Feb 20 '16 at 21:41
  • Oh, my bad. I stand corrected. But still, if you can only call the protected function from the same package, then there is really no one you'd possibly want to protect it from. – Oskar Hýbl Feb 20 '16 at 21:44
  • @OskarHýbl See [this question](http://stackoverflow.com/questions/18573016/understanding-javas-protected-modifier) for details. – fps Feb 20 '16 at 21:46
  • I need to protect me from myself :) Avoiding the confusion in a small snippet like I posted is easy. In larger code base the risk is greater. I could rely on naming conventions, such as prefixing hooks with do-; or I could add all-uppercase warnings to hooks' JavaDoc. But these solutions just decrease the risk of mistake, they are not 100% safe. I am still curious about solutions I might have missed and others propose, given the scenario above. – Peter Lényi Feb 20 '16 at 21:56

2 Answers2

3

You are indeed using the template pattern. There are two main things you could do to "hide" details from outside users:

1) Understand your access modifiers:

                   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     |  

Short of determined hackers bullying their way in past these protections with reflection, these can keep casual coders from accidentally calling methods that are best kept out of their way. The coders will appreciate this. No one wants to use a cluttered API.

Currently b.doWork() is protected. So it would only be visible in your main() if your main() is in class A (it's not), subclass B (it's not), or in the same package (not clear, you didn't post any clues about the package(s) your code resides in).

This begs the question, who are you trying to protect from seeing b.doWork()? If the package you're creating is something only you are developing it might not be so bad. If many people are stomping around in this package messing with many other classes it's not likely they will be intimately familiar with classes A and B. This is the case where you don't want everything hanging out where everyone can see it. Here it would be nice to only allow class and subclass access. But there is no modifier that allows subclass access without also allowing package access. As long as you're subclassing protected is the best you can do. With that in mind, don't go creating packages with massive numbers of classes. Keep packages tight and focused. That way most people will be working outside the package where things look nice and simple.

In a nutshell, if you'd like to see main() prevented from calling b.doWork() put main() in a different package.

package some.other.package

public final class Main {
...
}

2) The value of this next piece of advice will be hard to understand with your current code because it's about resolving issues that will only come up if your code is expanded on. But it really is closely tied to the idea of protecting people from seeing things like b.doWork()

What does "program to interfaces, not implementations" mean?

In your code what this means is it would be better if main looked like this:

public static void main(String[] args) {
  A someALikeThingy = new B(); // <-- note use of type A
  someALikeThingy.work();      //The name is silly but shows we've forgotten about B 
                               //and know it isn't really just an A :)
}

Why? What does using type A vs type B matter? Well A is closer to being an interface. Note, here I don't just mean what you get when you use the keyword interface. I mean type B is a concrete implementation of type A and if I don't absolutely have to write code against B I really would rather not because who knows what weird non A things B has. This is hard to understand here because the code in main is short & sweet. Also, the public interfaces of A and B aren't all that different. They both only have one public method work(). Imagine that main() was long and convoluted, Imagine B had all sorts of non A methods hanging off it. Now imagine you need to create a class C that you want main to deal with. Wouldn't it be comforting to see that while main was fed a B that as far as every line in main knows that B is just some simple A like thing. Except for the part after the new this could be true and save you from doing anything to main() but updating that new. Indeed, isn't this why using a strongly typed language can sometimes be nice?

<rant>
If you think even updating that part after new with the B() is too much work you're not alone. That particular little obsession is what gave us the dependency injection movement that spawned a bunch of frameworks that were really more about automating object construction than a simple injection that any constructor can let you do.
</rant>

Update:

I see from your comments that you are reluctant to create small tight packages. In that case consider abandoning an inheritance based template pattern in favor of a composition based strategy pattern. You still get polymorphism. It just doesn't happen for free. Little more code writing and indirection. But you get to change state even at run time.

This will seem counter intuitive because now doWork() has to be public. What kind of protection is that? Well now you can hide B entirely behind or even inside AHandler. This is some human friendly facade class that holds what used to be your template code but only exposes work(). A can just be an interface so the AHandler still doesn't know it has a B. This approach is popular with the dependency injection crowd but certainly doesn't have universal appeal. Some do it blindly every time which annoys many. You can read more about it here: Prefer composition over inheritance?

Community
  • 1
  • 1
  • Thank you for an exhaustive answer. I will try and address all of the ideas: 1) Access modifiers and package structure was the first thing I have considered long before posting the question. My package is already small, and splitting it further would give me 1- or 2-class packages as I have mentioned in one of my previous comments. So this is not a solution in my current case, but might be for someone else in the future. – Peter Lényi Feb 21 '16 at 11:18
  • 2) As for the interface approach, it has two downsides. Firstly, it does not work well with generics (I could provide an example). Secondly, it prevents you from writing: `Result r = new B(args).work();`, which I consider nicer than somewhat forced `A a = new B(args); Result r = a.work();`. This is again something I have tried and still went looking for something better, though it could be acceptable for some. – Peter Lényi Feb 21 '16 at 11:21
  • 3) Composition is truly the answer. I have jumped right to inheritance due to the semantics of my classes and forgot to consider this option. I am marking Mr. Schaffner's answer as correct as it provides an example, and keeps limited (protected) access to the hooks; but I recommend others to have a look at the links you have posted. – Peter Lényi Feb 21 '16 at 11:28
  • @PeterLényi I mostly agree to the concepts of this answer. In particular, programming to interfaces and favoring composition over inheritance. In my answer, I went straight to the point, though. – fps Feb 21 '16 at 12:35
2

The easiest and more obvious thing you could do would be to use your A -> B hierarchy from another package.

If for some reason you can't do it, then you could use an interface and make your B class wrap the class that actually descends from A. Something like this:

public interface Worker {

    void work();
}

public abstract class A implements Worker {

    @Override
    public final void work() {
        doPrepare();
        doWork();
    }

    protected abstract void doPrepare();

    protected abstract void doWork();
}

public static class B implements Worker {  // does not extend A, but implements Worker

    private final A wrapped = new A() { // anonymous inner class, so it extends A

        @Override
        protected void doPrepare() {
            System.out.println("doPrepare");
        }

        @Override
        protected void doWork() {
            System.out.println("doWork");
        }
    };

    @Override
    public void work() { // delegate implementation to descendant from A
        this.wrapped.work();
    }
}

This way, protected methods remain hidden in an anonymous inner class that extends from A, which is a private final member of B. The key is that B doesn't extends A, but implements the Worker interface's work() method by delegating to the anonymous inner class. So, even in the case B's work() method is being invoked from a class that belongs to the same package, protected methods won't be visible.

B b = new B();
b.work(); // this method is accessible via the Work interface

Output:

doPrepare
doWork
fps
  • 33,623
  • 8
  • 55
  • 110
  • I have tried and adjusted my code to follow this approach and it works like a charm, thank you. It works with generics, too. The only downside I have found is, that for each method in the abstract class A, you need to provide a wrapper; something you can avoid when you use inheritance. But this is definitely an agreeable trade-off. – Peter Lényi Feb 21 '16 at 11:12