'Implementing strategy when every strategy need different params

I have method that do 3 different thing based on user type, so i thought I can split it into strategies with factory that will return desired strategy based on user.type. So I will get:

strategy_interface.execute(product_id, user_id);

and then

strategy = strategy_factory.create(user.type);
strategy.execute(...);

but in reality method "execute" needs slighty different params for every user type:

if user.type is 1 then it needs only product_id
if user.type is 2 or 3 or 5 then it needs product_id and user_id
else it should only throw illegal_action_exception

I like my approach as it is easy to test. I have to check only instance type returned by factory but I have problem with that every returned strategy works differently. Maybe instead of universal factory I should do something like this:

if user.type is 1:
    strategy = new first_strategy();
    strategy.execute(...);
else if user.type is in(2, 3, 5):
    strategy = new second_strategy();
    strategy.execute(...);
throw new illegal_action_exception();


Solution 1:[1]

I think changing your design to an if structure is a step back. I suggest you to adopt context object pattern into your design. enter image description here

In my example, StrategyContext is a object that contains scoped data which is meaningful to your application. It encapsulates information you need for your different strategies. What it can holds depends on your application needs. I learn from your question that, for now there are ProductId and UserId.

Strategy.execute() could accept StrategyContext. In each implementation of AlphaStrategy, BetaStrategy, they can make use of the data inside StrategyContext and do their own business logic, without breaking the interface signature.

Of course, my answer is only a example. The data structure of StrategyContext does not have to be a map. And it does not have to be 'ContextScope' as the key. Sample code provided below.

import java.util.HashMap;
import java.util.Map;

// define the scope of context object for the application
enum ContextScope {
    ProductId, UserId

}

// StrategyContext store the data needed for Strategy
class StrategyContext {

    private Map<ContextScope, Object> context = new HashMap<ContextScope, Object>();

    public void set(ContextScope s, Object o) {
        this.context.put(s, o);
    }

    public Object get(ContextScope s) {
        return context.get(s);
    }
}

// Strategy interface
interface Strategy {
    public void execute(StrategyContext c);
}

// a mock Strategy which take only the product id
class AlphaStrategy implements Strategy {

    @Override
    public void execute(StrategyContext c) {
        System.out.println(String.format("Product Id:%s", c.get(ContextScope.ProductId)));
    }

}

// another mock Strategy which take both the product id and user id
class BetaStrategy implements Strategy {

    @Override
    public void execute(StrategyContext c) {
        System.out.println(
                String.format("Product Id:%s, User Id:%s", c.get(ContextScope.ProductId), c.get(ContextScope.UserId)));
    }

}

// The Factory to create different kinds of Strategy based on the user type. 
// The switch statement is not a good practice. But this is just a mock to make the demo runnable.  
class StrategyFactory {
    public Strategy create(int userType) {

        Strategy strategy = null;
        switch (userType) {
        case 1:
            strategy = new AlphaStrategy();
            break;
        case 2:
        case 3:
        case 5:
            strategy = new BetaStrategy();
            break;
        }

        return strategy;

    }
}

public class StrategyPattern {

    public static void main(String[] args) {

        StrategyFactory factory = new StrategyFactory();

        // AlphaStrategy
        System.out.println("AlphaStrategy - ");
        factory.create(1).execute(new StrategyContext() {
            {
                set(ContextScope.ProductId, 80);
            }
        });

        // BetaStrategy
        System.out.println("BetaStrategy - ");
        factory.create(5).execute(new StrategyContext() {
            {
                set(ContextScope.ProductId, 443);
                set(ContextScope.UserId, 200);
            }
        });
    }

}

Output

AlphaStrategy - 
Product Id:80
BetaStrategy - 
Product Id:443, User Id:200

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1