设计模式 | 基于工厂&策略模式的返奖流程实现

857 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

我们通过两篇文章《挑战工厂模式》《挑战策略模式》分别介绍了两种模式的模型、适用场景、优缺点。在我们实际开发业务中,往往单独使用一种模式是不能解决实际问题,有时我们需要根据实际需求组合模式达到目的,这篇文章我们来讲讲基于两种模式混合实现一个返奖逻辑。

一、返奖流程介绍

返奖流程的业务逻辑视图如下:

image.png

  • 首先我们要根据访问的用户Id确定用户的特征信息,比如新用户、老用户等等。
  • 接着要判断用户的特征,选择具体的返奖策略,并执行策略。
  • 返奖策略执行完成后,对所有的用户都有统一的处理逻辑,比如持久化,通知其它服务等等。

二、业务流程分析

通过流程分析,可以很清楚的看到,对于所有特征的用户,经历的返奖流程是保持一致的,唯一有区别或者说是有变化的是返奖规则。对于这样的业务流程,我们可参考开闭原则

Software entities (classes, modules, functions) should be open for extension but closed for modification。

官方定义开闭原则:软件实体(包括类、模块、功能等)应该对扩展开放,但是对修改关闭。对扩展开放,表示模块通过扩展的方式去应对需求的变化;对修改关闭,表示当需求变化时,应该尽可能关闭对模块源代码的修改。

所以套用开闭原则,我们应该对于返奖流程保持封闭,对于极有可能扩展的返奖规则保持开放。我们将返奖规则抽象为返奖策略,即针对不同用户类型的不同返奖方案,我们视为不同的返奖策略,不同的返奖策略会产生不同的返奖金额结果。

很显然,我们现在抽象了一个返奖策略,当然是要使用策略模式,然后返奖策略的产生需要统一产生,所以我们需要一个工厂,即使用工厂模式

三、代码实现

首先我们要定义一个抽象的策略,它能完成两件事情,第一步是执行返奖策略并获取金额,第二步是执行获得返奖策略后的操作。

public interface RewardStrategy {
    int rewardBy(String userTrait);
    void afterReward(String userTrait,int rewardAmount);
}

因为第二个步骤的执行对于所有用户执行逻辑都是一致的,所以准备一个抽象类去实现afterReward方法。

public abstract class AbstractRewardStrategy implements RewardStrategy{
    @Override
    public abstract int rewardBy(String userTrait);
​
    @Override
    public void afterReward(String userTrait, int rewardAmount) {
        //执行奖励策略后统一要处理的事情
        System.out.println("do something after reward");
    }
}

接着就是具体的返奖策略类,我们默认实现NewUserRewardStrategyOldUserRewardStrategy两个返奖策略。

public class NewUserRewardStrategy extends AbstractRewardStrategy{
    @Override
    public int rewardBy(String userTrait) {
        return 10;
    }
}
​
public class OldUserRewardStrategy extends AbstractRewardStrategy{
    @Override
    public int rewardBy(String userTrait) {
        //老用户还想有奖励?
        return 0;
    }
}

具体的策略准备好了,但是对于策略模式我们还要准备一个使用的context

public class RewardContext {
    final RewardStrategy rewardStrategy;
    
    public RewardContext(RewardStrategy rewardStrategy){
        this.rewardStrategy = rewardStrategy;
    }
​
    public void doStrategy(String userTrait){
        final int rewardAmount = rewardStrategy.rewardBy(userTrait);
        rewardStrategy.afterReward(userTrait,rewardAmount);
    }
}

至此,策略模式的部分完成,接着我们来实现工厂模式的部分。

public abstract class StrategyFactory<T> {
    abstract RewardStrategy createStrategy(Class<T> c);
}
​
public class FactorRewardStrategyFactory extends StrategyFactory {
    @SneakyThrows
    @Override
    RewardStrategy createStrategy(Class clazz) {
        return (RewardStrategy)clazz.newInstance();
    }
}

然后我们客户端的使用的代码如下。

public class RewardApp {
​
    static final String NEW_USER_TRAIT = "traitOfNewUser";
    static final String OLD_USER_TRAIT = "traitOfOldUser";
​
    public static void main(String[] args) {
        if(args == null || args.length ==0){
            throw new IllegalArgumentException("args error");
        }
        final String userTrait =  args[0];
        final FactorRewardStrategyFactory factorRewardStrategyFactory = new FactorRewardStrategyFactory();
        RewardStrategy strategy;
        if(userTrait.equals(NEW_USER_TRAIT)){
            strategy = factorRewardStrategyFactory.createStrategy(NewUserRewardStrategy.class);
        }else if(userTrait.equals(OLD_USER_TRAIT)){
            strategy = factorRewardStrategyFactory.createStrategy(OldUserRewardStrategy.class);
        }else{
            throw new IllegalArgumentException("Unknown userTrait");
        }
        final RewardContext rewardContext = new RewardContext(strategy);
        rewardContext.doStrategy(userTrait);
    }
}

至此,返奖流程实现完成,URL类图如下。

StrategyFactory.png

四、总结

策略模式保证了我们可以根据业务需求动态的切换策略,而不必改动我们的代码逻辑。工厂模式帮助我们去使用具体的策略,而不再关心创建对象过程中的逻辑。

通过两种模式的组合,当我们需要丰富策略时,我们只是需要继承实现AbstractRewardStrategy抽象类,不必动其它的代码逻辑。这样提高了服务的扩展性,在一定程度上做到了高内聚、低耦合。