Reputation: 253
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:
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
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
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
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
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
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