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:
I heard that composition is much better than inheritance.
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.
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?
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 ...
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).
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.
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?
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);
}
Related
I'm trying to implement a function that returns the maximum object of a given Comparable (generic) list.
I have 3 classes that I have implemented their compareTo method that returns the 1 if this is bigger than other, -1 if this is smaller than other, and 0 if they're equal.
Now my problem is with understanding with how do I work with a generic input COmparable list.
Here's the signature of my function, and the code I wrote so far (that refuses to work on me):
public static Comparable<?> getMax(List<Comparable<?>> ls) {
LinkedList<Comparable<?>> tmpComp = new LinkedList<Comparable<?>>();
for (Comparable<?> c : ls)
tmpComp.add(c);
Comparable<?> maxObj = tmpComp.get(0);
for (Comparable<?> c : tmpComp)
if (c.compareTo(maxObj) > 0)
m = c;
return m;
}
I'm writing a system that has users in it, and ads. Users and ads both classes that have "profit" field on them that all I do in my compareTo methods is to compare which of the two (this, or other) have more profit and then just returns the right value according to that. The 3rd class is compared via another field, which is an int as well, that indicates the level (int) of the Quest.
Also that if statement, specifically, gives me an error of the type "is not applicable for the arguments".
Any clues?
Thanks in advance!
Reading your comment, I suggest you redesign your model to be:
interface ProfitGenerating {
double getProfit();
}
class User implements ProfitGenerating {
...
}
class Advert implements ProfitGenerating {
...
}
List<ProfitGenerating> profits = ...;
Optional<ProfitGenerating> maxProfit = profits.stream()
.max(Comparator.comparingDouble(ProfitGenerating::getProfit));
The answer by Mạnh Quyết Nguyễn is good. But it does not account for the situation where you have multiple potential types T, which appears to be your situation.
So in that situation, just wrap your various classes with a single class and use his solution.
If you have a User class and an Ad class, then create a wrapper like so:
class ProfitMaker implements Comparable<ProfitMaker> {
User user;
Ad ad;
public int compare(ProfitMaker p) {
//check my profit and compare with profit of p
}
}
Use that class as the "T" when usign the getMax from Mạnh Quyết Nguyễn.
Alternatively, use an interface
interface ProfitMaker extends Comparable<ProfitMaker> {
int getProfit();
}
Make both your User and Ad classes implement that interface, and that use that interface as the "T" along with the getMax method from Mạnh Quyết Nguyễn.
Your three classes must be comparable to each other. For this they will need to implement Comparable<SomeX> where SomeX is their lowest common superclass. In the worst case, SomeX is Object.
If this is the case, you can simply do:
ls.stream().max(Comparator.naturalOrder())
Alternatively, instead of forcing your classes to implement Comparable<...>, you could capture comparison semantics in a Comparator<...> and then do:
ls.stream().max(comparator)
Using a comparator is better for cases where the order is not really "natural" for the type or where there may be different orders. I think this is the case here since you actually compare instances of different types. It is hard to argue that some order is "natural" for these instances as they don't even belong to one type.
If you compare your instances based on some property they share (like int getProfit()), it would make sense creating a common interface like Profitable. Then you could do:
ls.stream().max(Comparator.comparintInt(Profitable::getProfit))
Note that if you compare on privitive types, you should use comparingInt/comparingLong/comparingDouble instead of comparing to avoid unnecessary boxing and unboxing.
If you for some reason can't create and implement a common interface like Profitable, you can still use comparingInt and likes. You'll just have a much uglier lambda:
ls.stream().max(Comparator.comparintInt(l -> {
if (l instanceof Ad) { return ((Ad) l).getProfit(); }
else if (l instanceof Ransom) { return ((Ransom) l).getProfit(); }
// ...
else { throw new IllegalArgumentException(...); }
}))
In Factory method pattern there are 2 lead implementation (correct me if I'm wrong):
When Creator class is being abstract and not providing an implementation for the Factory method:
public abstract class CasinoGameCreator {
public void playGame() {
ICasinoGameType gameType = createGame();
gameType.play();
}
public abstract ICasinoGameType createGame();
Or, we can have the Creator class be a concrete class that provides implementation for the Factory method:
public class CasinoGame {
public static CasinoGame createGame(GameType type) {
if (type == GameType.BlackJack) {
return new BlackJackGame();
} else if (type == GameType.Poker) {
return new PokerGame();
} else {
return null;
}
}
}
Is there any strong preference when to use each implementation? if there is, in what general situations we whould prefer using the 1st approach over the 2nd?
Option 1 is following the Open/closed principle. This means: it is open for extensions (as different subclasses can implement different ways of creating a game); but it is closed for modification - the behavior of playGame() is fixed. Well, it is not; but if you use this pattern, you really would want to make playGame() to be final. If you have such an abstract class with an implementation X; and an abstract method Y (used within the other method X); than it doesn't much sense to allow subclasses to change X.
Option 2 is helpful when you are really sure about the different type of games; meaning: chances that this enum will ever change are small. Given the idea of games in casino; I very much doubt that this would be true here. Probably you could add a new game every other day. And then, you have to turn to each place that switches over the GameType and adapt that code.
So, given those thoughts, option 1 would be the first choice - because you can simply add a new game type by creating a new subclass of your creator class. This means: you can add a new game without touching the code responsible for other games.
Of course: if you would pick a different example, the requirements might be different, and then option 2 might have certain benefits.
Can somebody explain me what "hidding api with interfaces in Java" means ?
How can I use the API functions by means of interfaces ?
I would need an small working example to understand the how the interfaces hides the api non public parts, and how can I use the api puplic parts in the same time.
Thanks in advance.
THANKS GUYS FOR THE QUICK REPLY, GIVE ME TIME TO THINK OVER THE ANSWERS.
LAST BUT NOT LEAST THANKS FOR YOUR TIME AND EFFORT!
II. My second question is : What happens in the background this case below ?
IBinaryOperation op = BinaryOperationProvider.getOperation("multiply");
or
List<String> myList = new LinkedList<String>();
Its not clear for me because the interfaces consist of methods' declarations that's why i dont understand what could happened in the lines above.
Is there any meaning of the equality between empty method of used interfaces and objects ?
Sorry for my weak English.
For instance, you may declare and create a list of strings as follows:
List<String> myList = new LinkedList<String>();
List is the type of myList. It is an interface. It means that all subsequent calls to methods of myList will be done through the interface: you may only call methods declared in the List interface.
However, the concrete class of the object is LinkedList, that contains more methods, some of them reflecting the structure of a linked list (for instance addFirst and addLast). But these methods are hidden because of the way you declared the variable. You chose to access the object through a given (restrictive) interface.
It may seem restrictive, but it also means that you can change your code at any time, and replace the concrete class LinkedList with any other class that implements the List interface, for example ArrayList.
Usually when you expose your API, you should hide the implementation details as much as possible and expose it via simple interfaces.
For e.g. Suppose that you give an api for adding two numbers.
Soln1 (Bad soln) Give the following class to client
public class Adder {
public void setA() {..}
public void setB() {..}
public int add() { return A + B; }
}
Soln 2 (better soln): Give the following interface to the client.
public interface Adder {
public int add(int a, int b);
}
Now why is soln 2 a better solution. If you had given user the first soln. The client is bound to the class Adder. Suppose later you have a new implementation of addition that could add the numbers in the cloud(over-imaginative :)), you may have to as the client to change their code to use the new class.
Instead if you just give them the interface, you could provide many implementation and have a factory mechanism to choose the suitable implementation.
Here's a very simple example that uses an interface:
public interface IBinaryOperation {
public int performOp(int a, int b);
}
private class MultiplicationProvider implements IBinaryOperation {
public int performOp(int a, int b) {
return a * b;
}
}
public class BinaryOperationProvider {
static IBinaryOperation getOperation(String name) {
if ("multiply".equals(name)) {
return new MultiplicationProvider();
} else if ("add".equals("name)) {
return new AdditionProvider();
} // ...
}
}
You would use this like:
IBinaryOperation op = BinaryOperationProvider.getOperation("multiply");
int c = op.performOp(a, b);
In the above example, MultiplicationProvider is completely private to the implementation of the API. The only public part is the IBinaryOperation interface, and the BinaryOperationProvider class.
Just to be clear, what's "hidden" is not the API, but the implementation. Most clients of List (to use an example above) don't need to know which kind of List is actually being used. (Just like most TCP/IP clients don't need to know anything in particular about the network they're using -- just that it supports the standard connection abstraction.)
I've recently discovered an interesting way to create a new instance of an object in Google Guava and Project Lombok: Hide a constructor behind a static creator method. This means that instead of doing new HashBiMap(), you do HashBiMap.create().
My question is why? What advantage do you have of hiding the constructor? To me I see absolutely no advantage of doing this, and it seems to break basic object creation principles. Since the beggining you create an object with new Object(), not some Object.createMe() method. This almost seems like creating a method for the sake of creating a method.
What do you gain from doing this?
There are a number of reasons why you might prefer a static factory method instead of a public constructor. You can read Item 1 in Effective Java, Second Edition for a longer discussion.
It allows the type of the object returned by the method to be different than the type of the class that contains the method. In fact, the type returned can depend on the parameters. For example, EnumSet.of(E) will return a different type if the emum type has very few elements vs if the enum type has many elements (Edit: in this particular case, improving performance for the common case where the enum doesn't have many elements)
It allows caching. For instance, Integer.valueOf(x) will, by default, return the same object instance if called multiple times with the same value x, if x is between -128 and 127.
It allows you to have named constructors (which can be useful if your class needs many constructors). See, for example, the methods in java.util.concurrent.Executors.
It allows you to create an API that is conceptually simple but actually very powerful. For instance, the static methods in Collections hides many types. Instead of having a Collections class with many static methods, they could have created many public classes, but that would have been harder for someone new to the language to understand or remember.
For generic types, it can limit how much typing you need to do. For example, instead of typing List<String> strings = new ArrayList<String>() in Guava you can do List<String> strings = Lists.newArrayList() (the newArrayList method is a generic method, and the type of the generic type is inferred).
For HashBiMap, the last reason is the most likely.
This is usually done because the class actually instantiated by the create() method might be different than the type upon which you are invoking the method. i.e. a factory pattern where the create() method returns a specific subclass that is appropriate given the current context. (For example, returning one instance when the currrent environment is Windows, and another when it is Linux).
Unlike constructors, static methods can have method names. Here's a recent class I wrote where this was useful:
/**
* A number range that can be min-constrained, max-constrained,
* both-constrained or unconstrained.
*/
public class Range {
private final long min;
private final long max;
private final boolean hasMin;
private final boolean hasMax;
private Range(long min, long max, boolean hasMin, boolean hasMax) {
// ... (private constructor that just assigns attributes)
}
// Static factory methods
public static Range atLeast (long min) {
return new Range(min, 0, true, false);
}
public static Range atMost (long max) {
return new Range(0, max, false, true);
}
public static Range between (long min, long max) {
return new Range(min, max, true, true);
}
public static Range unconstrained () {
return new Range (0, 0, false, false);
}
}
You couldn't do this using just constructors, as atLeast and atMost would have the exact same signature (they both take one long).
This is called a Factory method pattern. Where the factory lies within the class itself. Wikipedia describes it pretty well but here are a few snippets.
Factory methods are common in toolkits and frameworks where library code needs to create objects of types which may be subclassed by applications using the framework.
Parallel class hierarchies often require objects from one hierarchy to be able to create appropriate objects from another.
Well it would be possible for SomeClass.create() to pull an instance from a cache. new SomeClass() won't do that without some shenanigans.
It would be also be possible for create() to return any number of implementations of SomeClass. Basically, a Factory type of dealio.
Although not applicable to this particular code example, the practice of hiding the constructor behind a static method is Singleton Pattern. This is used when you want to ensure that a single instance of the class is created and used throughout.
There are many reasons to use this factory method pattern, but one major reason Guava uses it is that it lets you avoid using type parameters twice when creating a new instance. Compare:
HashBiMap<Foo, Bar> bimap = new HashBiMap<Foo, Bar>();
HashBiMap<Foo, Bar> bimap = HashBiMap.create();
Guava also makes good use of the fact that factory methods can have useful names, unlike constructors. Consider ImmutableList.of, ImmutableList.copyOf, Lists.newArrayListWithExpectedSize, etc.
It also takes advantage of the fact that factory methods don't necessarily have to create a new object. For instance, ImmutableList.copyOf, when given an argument that is itself an ImmutableList, will just return that argument rather than doing any actual copying.
Finally, ImmutableList's factory methods return (non-public) subclasses of ImmutableList such as EmptyImmutableList, SingletonImmutableList and RegularImmutableList depending on the arguments.
None of these things are possible with constructors.
i got very interesting reason to hide constructor check it and please let me know if there is any other alternative to achieve this
enter code here
Class A
{
String val;
protected A( )
{
}
protected A(String val)
{
this.val=val;
}
protected void setVal( String val)
{
this.val=val;
}
public String getVal()
{
return val;
}
}
class B extends A
{
B()
{
super();
}
public val setVal(String val)
{
super.val=val;
}
}
class C extends A
{
C(String val)
{
super(val);
}
}
Some main reasons
Primarily it gives you the power to instantiate a different (sub) class
Possibility to return null
It enables you to return an already existing object
The following code sample is an implementation of the Strategy pattern copied from Wikipedia. My full question follows it...
The Wiki's main method:
//StrategyExample test application
class StrategyExample {
public static void main(String[] args) {
Context context;
// Three contexts following different strategies
context = new Context(new ConcreteStrategyAdd());
int resultA = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategySubtract());
int resultB = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategyMultiply());
int resultC = context.executeStrategy(3,4);
}
}
The pattern pieces:
// The classes that implement a concrete strategy should implement this
// The context class uses this to call the concrete strategy
interface Strategy {
int execute(int a, int b);
}
// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyA's execute()");
return a + b; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyB's execute()");
return a - b; // Do a subtraction with a and b
}
}
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyC's execute()");
return a * b; // Do a multiplication with a and b
}
}
// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {
private Strategy strategy;
// Constructor
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
Considering specifically the above example, is the Context class redundant?
For example, I can come up with the following alternate main implementation by using the existing classes and interface except Context and it will work exactly the same. It is still loosely coupled.
(( Edit: In this simple scenario, when I leave out the Context class, will I be making a future mistake? ))
public static void main(String[] args) {
IStrategy strategy;
// Three strategies
strategy = new ConcreteStrategyAdd();
int resultA = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategySubtract();
int resultB = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategyMultiply();
int resultC = strategy.executeStrategy(3,4);
}
Summary Update
Listing in point form what was discovered through answers and comments:
The Context allows for variation in how the composited Strategy is used (e.g. timing of it's call). Different Contexts might do different internal work before and after calling the given Strategy.
The context is a high level "black box". The Context logic can change, also the composited Strategy can change (or a different one used) without breaking the client because the client understands only how to call the context.
Even though I created an alternate implementation of the Wikipedia sample code by leaving out the Context, and although it worked the same as the original, the entire situation was simplified (in both cases) and my changes actually meant: 1. it's not a Strategy pattern anymore, 2. I miss the benefits of the spirit of the Strategy pattern that are mentioned here.
My alternate implementation used the main method like a Context, so I might as well keep the Context if effectively simulating it. By creating an impure Strategy pattern, confusion was created. I didn't need to reinvent the wheel or try to be smarter (in this case).
If any other points would be useful or if this needs correction leave a comment and I'll modify the list accordingly.
As the name suggests, the Context is what encapsulates the point at which the strategy is performed. Without that, you just have a naked Strategy, and the calling class now picks up an extra responsibility: knowing when to call the Strategy itself. Your example is perhaps a bit too simple, and in this particular case, I would say that the Context isn't getting you too much.
An example that perhaps better illustrates the usefulness of a Context is more like the following:
public class LoadingDock { // Context.
private LoadStrategy ls; // Strategy.
public void setLoadStrategy(LoadStrategy ls) { ... }
// Clients of LoadingDock use this method to do the relevant work, rather
// than taking the responsibility of invoking the Strategy themselves.
public void shipItems(List<ShippingItem> l) {
// verify each item is properly packaged \
// ... | This code is complex and shouldn't be
// verify all addresses are correct | subsumed into consumers of LoadingDock.
// ... | Using a Context here is a win because
// load containers onto available vehicle | now clients don't need to know how a
Vehicle v = VehiclePool.fetch(); // | LoadingDock works or when to use a
ls.load(v, l); // / LoadStrategy.
}
}
Notice how the Strategy will never be called directly from an external client. Only shipItems uses the strategy, and the details of the steps it follows are a black box. This allows the Context to adjust how it uses the strategy without affecting clients. For instance, the steps could be completely reordered or adjusted (or removed entirely) to meet performance objectives or other goals -- but for the client, the external interface of shipItems() looks exactly the same.
Notice, also, that our example Context, the LoadingDock, could change its LoadStrategy at any time based on its internal state. For example, if the dock is getting too full perhaps it will switch to a more aggressive scheduling mechanism that gets crates off the dock and into trucks faster, sacrificing some efficiency in doing so (maybe the trucks don't get loaded up as efficiently as they could have been).
This is the better example of how the real "Context " class can look in this scenario:
class Accumulator {
private Strategy strategy;
public Accumulator(Strategy strategy) {
this.strategy = strategy;
}
public int accumulate(List<Integer> values) {
int result = values.get(0);
for (int i = 1; i < values.size(); i++) {
result = strategy.execute(result, values.get(i));
}
return result;
}
}
EDIT: Typo in constructor fixed
It might be for this made-up example, but then I wouldn't call this the ne plus ultra of Strategy.
The Context class is demonstrating how you can give a class different behavior simply by passing in a new concrete implementation of the interface. Since the class only knows the interface, nothing has to change. That's the point. Don't take the rest of the example too literally.
The way you coded it will work, but the point is that you've dumped this into a main method. That won't be the way you'll typically use Strategy. You'll be doing it within a class, and Context is a simple example of that.
Context won't be redundant in Strategy pattern and it is useful in below scenarios:
The code to call a particular Strategy is spread in multiple classes without invoking Context . In future, if you have to re-factor or change the Strategy interface, it will be come a hectic task.
Assume that some data need to be populated before calling a particular Strategy. Context is best fit here by providing additional information and call Strategic method of a particular Strategy.
e.g. Context will get an Strategy and userId as parameter. Before executing Strategy, Context need to provide lot of additional information related to User Profile. Context will fetch the required information and executes strategic method of Strategy. In absence of Context, you have to duplicate the code at 100 different places if you call strategic method at 100 different places.
Context can take independent decision on which Strategy to invoke. It can simple change the Strategy type depending on run time configuration. Strategy core USP is switching between family of related algorithm. Context is the best place to achieve it.
If you have to act on multiple Strategies , Context is the best place. axtavt proposed answer of using Accumulator is one example.
Refer to this post more details.
Real World Example of the Strategy Pattern