当每个策略需要不同的参数时执行策略

Implementing strategy when every strategy need different params

我有一种方法可以根据用户类型做 3 种不同的事情,所以我想我可以将其拆分为工厂策略,这些策略将 return 基于 user.type 所需的策略。所以我会得到:

strategy_interface.execute(product_id, user_id);

然后

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

但实际上方法“execute”对于每种用户类型需要略微不同的参数:

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

我喜欢我的方法,因为它很容易测试。我必须只检查工厂 returned 的实例类型,但我有一个问题,即每个 returned 策略的工作方式都不同。也许我应该做这样的事情而不是通用工厂:

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();

我认为将您的设计更改为 if 结构是一种倒退。我建议您在设计中采用上下文对象模式。

在我的示例中,StrategyContext 是一个包含对您的应用程序有意义的范围数据的对象。它封装了您不同策略所需的信息。它可以容纳什么取决于您的应用需求。我从你的问题中得知,目前有 ProductIdUserId.

Strategy.execute() 可以接受 StrategyContext。在 AlphaStrategyBetaStrategy 的每个实现中,他们都可以利用 StrategyContext 中的数据并执行自己的业务逻辑,而不会破坏接口签名。

当然,我的回答只是一个例子。 StrategyContext 的数据结构不一定是映射。并且它不必是 'ContextScope' 作为键。下面提供了示例代码。

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);
            }
        });
    }

}

输出

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