当每个策略需要不同的参数时执行策略
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
是一个包含对您的应用程序有意义的范围数据的对象。它封装了您不同策略所需的信息。它可以容纳什么取决于您的应用需求。我从你的问题中得知,目前有 ProductId
和 UserId
.
Strategy.execute()
可以接受 StrategyContext
。在 AlphaStrategy
、BetaStrategy
的每个实现中,他们都可以利用 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
我有一种方法可以根据用户类型做 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
是一个包含对您的应用程序有意义的范围数据的对象。它封装了您不同策略所需的信息。它可以容纳什么取决于您的应用需求。我从你的问题中得知,目前有 ProductId
和 UserId
.
Strategy.execute()
可以接受 StrategyContext
。在 AlphaStrategy
、BetaStrategy
的每个实现中,他们都可以利用 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