0

I am trying to build an algorithm that works in different ways depending on a traversal strategy and an update strategy. However, not every update Strategy works with every traversal strategy. Hence, I figured that an update strategy must only be instantiated with a corresponding traversal strategy. I wanted to force a constructor for that (see below). So that the subclasses would have to check if they support the strategy.

I am currently having an Interface

public interface TraversalStrategy {
...
}

And an (invalid) abstract class

public abstract class UpdateStrategy {
protected TraversalStrategy travStrategy;

public abstract UpdateStrategy(TraversalStrategy travStrategy);
}

What is the correct way to imply such a dependency? I could of course add an empty body to this constructor but that seemed wrong to me.

Update: Inspired by the Answer of @Kayaman, I created a new class TestcaseGenerator that is used to construct a valid combination.

public TestcaseGenerator(TraversalStrategy travStrategy, UpdateStrategy updStrategy){
    if (updStrategy.supports(travStrategy)){
        this.travStrategy = travStrategy;
        this.updStrategy = updStrategy;
    }
}

Current Class Diagram

What I don't like about this yet is, that it would now be unnecessary to give the instance of TraversalStrategy to the UpdateStrategy in order to check if it is supported. I would rather only need the class name. Can you tell me how to achieve that? Experiments with .getClass().getName() seemed horrible. Currently I am doing:

public boolean supports(TraversalStrategy travStrategy){
   if(travStrategy instanceof UpstreamTraversalStrategy){ 
       return true; 
   }
   return false;
}
Koronis
  • 49
  • 5
  • 2
    what's your question? – eliasah Oct 25 '15 at 19:20
  • Are all the possible update and traversal strategies (and which strategies are compatible with each other) known compile-time? – Mick Mnemonic Oct 25 '15 at 19:23
  • @PinkieSwirl Actually it's a syntax error. – Kayaman Oct 25 '15 at 19:25
  • @PinkieSwirl It does not have return type and is marked abstract.Neither a constructor nor a method – Kumar Abhinav Oct 25 '15 at 19:26
  • @KumarAbhinav you are right, I missed that there was no return type, thanks. – Pinkie Swirl Oct 25 '15 at 19:28
  • I'm confused. Did you delete your comment @PinkieSwirl? @MickMnemonic: Yes they are known at compile-time. – Koronis Oct 25 '15 at 19:46
  • Then consider encapsulating the strategies as `enum`s. – Mick Mnemonic Oct 25 '15 at 19:49
  • @Koronis yes, to make things clear again: `public abstract UpdateStrategy(TraversalStrategy travStrategy);` is a syntax error. It is no constructor, since constructors can't be abstract and it is no abstract method, since a return type is missing. – Pinkie Swirl Oct 25 '15 at 19:53
  • Oh I tried to use enums a couple of times at other occassions and removed them again when I was done in the past. Do you mean the supported combinations? But I can't write something like `public enum Combinations{ One(UpdateStrategy1, TraversalStrategy1), Two(UpdateStrategy1, TraversalStrategy2), Three(UpdateStrategy1, TraversalStrategy3),...` – Koronis Oct 25 '15 at 19:57
  • @PinkieSwirl: Alright yeah, that's why I wrote (invalid) :) . Thanks for adding the explanation for others! – Koronis Oct 25 '15 at 19:59
  • No, I meant `public enum Updater implements UpdateStrategy` which would actually contain the implementations and list the supported traversal strategies. – Mick Mnemonic Oct 25 '15 at 20:03
  • @MickMnemonic I afraid I don't know enough about enums to understand how I would have to implement your solution without a bigger picture. – Koronis Oct 26 '15 at 10:09
  • Are the different strategies always paired, i.e. do they have a 1-to-1 relationship (as in your "Upstream" example)? Or could `UpstreamUpdateStrategy` additionally support some other way of traversal than `UpstreamTraversalStrategy`? – Mick Mnemonic Oct 26 '15 at 10:25
  • @MickMnemonic No they aren't paired. Every `UpdateStrategy` can potentially support multiple `TraversalStrategy` s. – Koronis Oct 26 '15 at 11:54

2 Answers2

1

One common way is to have the superclass constructor call an abstract method such as isSupported(TraversalStrategy t); and fail if it's not true.

The subclasses would then implement the method accordingly by using instanceof or any other way to determine if the strategy is a supported one.

One approach would be to create a third class with a Builder pattern approach. Instead of providing TraversalStrategy as a parameter to UpdateStrategy, they would both be included in the third object (and they could be checked at build() to prevent incompatible strategies).

You could then have general functionality in the third class, with the strategy classes becoming lighter.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • 2
    I was just about to answer in the same way, but then I remembered Joshua Bloch saying that it's always wrong to call an overridable method from a constructor. http://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors – Paul Boddington Oct 25 '15 at 19:30
  • Thanks, that sounds like a valid idea. I'm gonna experiment and give feedback tomorrow :) . – Koronis Oct 25 '15 at 19:48
1

Even an abstract class must have a valid constructor. Even through it is not possible to create an instance of an abstract class, a non abstract subclass always calls the constructor of the super class first. Therefore your constructor on the abstract class needs a body to initialize the TraversalStrategy.

hotzst
  • 7,238
  • 9
  • 41
  • 64
  • That's how I currently have it implemented. But I feel this makes people think that there is already some kind of checking in the super(TraversalStrategy travStrategy) constructor. And that's not true. I want to make sure that future programmers understand what they have to do with the constructor. Or find a better solution. :) – Koronis Oct 25 '15 at 19:52
  • So put it down as a contract in the JavaDoc of the constructor. This will document the API which will be most relevant for any future developer using that abstract class. – hotzst Oct 25 '15 at 20:13