How to get rid of the inheritance?

I have an algorithm, and I have 2 different implementations of the algorithm. These implementations should be called from many places, depending on the mode selected by the user. I wouldn't like to write conditional statements at all places where implementations called. So, I create an abstract class and Implementations inherit it. I can set the desired mode in one place like this:

if(firstMode){
    list = new ListForm1();
}
else{
    list = new LiastForm2();
}

And after that in all other places I can enjoy all the benefits of polymorphism. It works good but I want to get rid of the inheritance of the following reasons:

  1. I heard that composition is much better than inheritance.
  2. The first form of the algorith is much easier then the second form. In the first form I have only 3 methods and in second form I have 15 methods. The abstract class had to include all 15 (and 5 common methods). It turns out that the 12 methods not using by the first form.
  3. Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class.

The Strategy Pattern, as I understand, does not make sense to use here. Here is the example of Strategy Pattern:

//abstract strategy
    interface Strategy {
        int execute(int a, int b); 
    }

// concrete strategy1
class ConcreteStrategyAdd implements Strategy {

    public int execute(int a, int b) {
        return a + b;  
    }
}

// concrete strategy2
class ConcreteStrategySubtract implements Strategy {

    public int execute(int a, int b) {
        return a - b;  
    }
}

//concrete strategy3
class ConcreteStrategyMultiply implements Strategy {

    public int execute(int a, int b) {
        return a * b; 
    }    
}

class Context {

    private Strategy strategy;

    public Context() {
    }

    // Set new concrete strategy
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    // use strategy
    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}

It has the same problems. Strategies should be linked with each other. If I link them with the interface instead of an abstract class it will be even worse. Interface will contain a lot of methods but many of them will not be needed for the first form of the algorithm. In addition, general methods have to duplicate in all concrete strategies. I can not provide a default implementation in the interface.

Moreever, I don't understand how to use composition here. As I understand, Strategy Pattern already used composition. Class Context includes the instance of Strategy as a field. But maybe it is delegation.

So, here is my question:

Can I get rid of all the above problems (too many methods of an abstract class, the strong connection, because of which it will be difficult to add a new form of an algorithm), but still use conditional statements in only one place, not in all cases when I need some form of algorithm.

UPD: I want to show how I called some methods, which implemented in SECOND form of the algorithm, but not need for the FIRST form of algorithm:

if (list.getCurrentLeaders().contains(ballIdx))

The default implementation of method getCurrentLeaders() return null. So, if I called it with instance of the FIRST form of the algorithm then I will get an error. I understand that it is bad. But how can I solve it?

Upvotes: 3

Views: 300

Answers (5)

D Stanley
D Stanley

Reputation: 152634

I heard that composition is much better than inheritance.

Not always - many times inheritance is the right construct. You have to think about it in has a and is a terms. A football team has a collection pf players. It also has a coach, a schedule, a name, etc. So Team : List<Player> is not the right construct.

A Car is a Vehicle, so inheritance is the right construct.

So think about your design this way:

Do my classes share a common base? Is there a base class that makes sense to say ListForm1 is a ListBase and ListForm2 is a ListBase. What methods and properties are common to those types that should be in the case type? What methods and properties should be virtual so that I can override them?

The first form of the algorithm is much easier then the second form. In the first form I have only 3 methods and in second form I have 15 methods. The abstract class had to include all 15 (and 5 common methods). It turns out that the 12 methods not using by the first form.

So maybe your base type only 3 methods, and you add methods in the sub-types as necessary. Remember that you can have multiple base types in the chain, but it's a chain, not a tree, meaning you can have a single parent that has another parent, but you can't have two parents.

Or maybe you have orthogonal interfaces since you can implement multiple interfaces.

Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class.

Why? Why can't the new algorithm just define its own methods that it needs, so long as clients pick the appropriate level in the inheritance chain (or appropriate interface(s)) so that it knows what methods should be implemented.

if (list.getCurrentLeaders().contains(ballIdx))

The default implementation of method getCurrentLeaders() return null. So, if I called it with instance of the FIRST form of the algorithm then I will get an error. I understand that it is bad. But how can I solve it?

So do you need to check that this particular list implements an interface (or inherits a base class) that does implement that method?

Upvotes: 1

Dzianis Yafimau
Dzianis Yafimau

Reputation: 2016

You can implement some kind of Chain Of Responsibility pattern.

interface IStrategy {
  void Run();
  bool CanHandle(IContext context);
}

class StrategyChecker {
  IStrategy GetStrategy(IContext context) {
    foreach(var strategy in strategies) {
      if(strategy.CanHandle(context)
        return strategy;
    }

    return defaultStrategy;
  }
}    

class Director {
  void Run() {
    strategyChecker.AddStrategy(strategy1);
    strategyChecker.AddStrategy(strategy2);

    var strategy = strategyChecker.GetStrategy(someContext);
    strategy.Run();
  }
}

Sorry for c# pseudo-code.

Upvotes: 1

Oliver Watkins
Oliver Watkins

Reputation: 13539

If you are not implementing all the methods (ie. if you have 15 methods in the abstract class to be implemented, and you only need to implement 10), then you are breaking the Liskov Substitution Principle :

https://en.wikipedia.org/wiki/Liskov_substitution_principle

Basically, that is a bad thing.

Try and convert the non-common methods into some other kind of object that gets passed into the constructor (on the abstract).

Upvotes: 1

aleroot
aleroot

Reputation: 72686

Starting from the beginning in the case you need to call a different algorithm based on a different mode chosen by the user you could create a kind of factory class to supply the algorithm throughout your code. I think that if it is only an algorithm and if you are on Java 8 you can use a Function or a Predicate or a Supplier in combination with a map to avoid the if statement, for example :

Map<String, Predicate<Whatever>> map = new HashMap<>();
map.put("mode_one", (w) -> true);
map.put("mode_two", (w) -> false);

Then to call the algorithm, simply :

map.get("mode_one").test()

In the case you need to supply a different form like in the example you posted, you could use a Supplier instead of a predicate. Based on your simple requirement, I think that going functional would be the best bet ...

Upvotes: 1

Mateen Ulhaq
Mateen Ulhaq

Reputation: 27271

Why not just use your IStrategy as a type?

interface IStrategy {
    int execute(int a, int b); 
}

class Strategy1 implements IStrategy {}
class Strategy2 implements IStrategy {}

static class StrategyFactory {
    IStrategy Create(bool first) {
        return first ? new Strategy1() : new Strategy2();
    }
}

And then in your user code:

void doStuff()
{
    IStrategy myStrategy = StrategyFactory.Create(true);
    myStrategy.execute(1, 2);
}

Upvotes: 0

Related Questions