theme: vue-pro
这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
我们通过两篇文章《挑战工厂模式》、《挑战策略模式》分别介绍了两种模式的模型、适用场景、优缺点。在我们实际开发业务中,往往单独使用一种模式是不能解决实际问题,有时我们需要根据实际需求组合模式达到目的,这篇文章我们来讲讲基于两种模式混合实现一个返奖逻辑。
一、返奖流程介绍
返奖流程的业务逻辑视图如下:
- 首先我们要根据访问的用户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"); } }
接着就是具体的返奖策略类,我们默认实现NewUserRewardStrategy
和OldUserRewardStrategy
两个返奖策略。
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
类图如下。
四、总结
策略模式保证了我们可以根据业务需求动态的切换策略,而不必改动我们的代码逻辑。工厂模式帮助我们去使用具体的策略,而不再关心创建对象过程中的逻辑。
通过两种模式的组合,当我们需要丰富策略时,我们只是需要继承实现AbstractRewardStrategy
抽象类,不必动其它的代码逻辑。这样提高了服务的扩展性,在一定程度上做到了高内聚、低耦合。