This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Through two articles “Challenge Factory Mode” and “Challenge Strategy Mode”, we introduce the models, applicable scenarios, advantages and disadvantages of the two modes respectively. In our actual development business, it is often impossible to use one pattern alone to solve the actual problem. Sometimes we need to combine the pattern according to the actual requirements to achieve the purpose. In this article, we will talk about the realization of a reward logic based on the mixing of the two patterns.

1. Introduction of reward process

The business logic view of the reward process is as follows:

  • First, we need to determine the characteristics of the user based on the user Id, such as new users, old users, and so on.
  • Then judge the characteristics of the user, select a specific reward strategy, and implement the strategy.
  • Once the reward policy is implemented, there is a unified logic for all users, such as persistence, notification to other services, and so on.

Second, business process analysis

Through the process analysis, it can be clearly seen that for all characteristics of users, the reward process is consistent, the only difference or change is the reward rules. For such a business process, we can refer to the open closed principle.

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

Officially defines the open and closed principle: software entities (including classes, modules, functions, etc.) should be open for extension, but closed for modification. Open to extension, express the module through the extension way to respond to the change of requirements; Closed for modification means that modifications to the module source code should be closed whenever possible when requirements change.

So by applying the open closed principle, we should keep the reward process closed and open to the possibility of expanding the reward rules. We abstract the reward rules as reward strategies, that is, different reward schemes for different user types are regarded as different reward strategies, and different reward strategies will produce different reward amount results.

Obviously, we now abstract a reward strategy, using the strategy pattern, and then the generation of the reward strategy needs to be uniformly generated, so we need a factory, using the factory pattern.

Three, code implementation

First we need to define an abstract policy that does two things. The first step is to execute the reward policy and get the amount, and the second step is to execute the action after the reward policy is obtained.

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

Since the execution of the second step is consistent for all user execution logic, prepare an abstract class to implement the afterReward method.

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"); }}Copy the code

Next is the specific reward policy class, we implement NewUserRewardStrategy and OldUserRewardStrategy by default two reward strategies.

public class NewUserRewardStrategy extends AbstractRewardStrategy{ @Override public int rewardBy(String userTrait) { return 10; } } public class OldUserRewardStrategy extends AbstractRewardStrategy{ @Override public int rewardBy(String userTrait) { // Do you want a reward? return 0; }}Copy the code

The specific policy is ready, but for the policy pattern we also need to prepare a context to use.

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); }}Copy the code

At this point, the strategy pattern part is complete, and we move on to the factory pattern part.

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(); }}Copy the code

Then our client uses the following code.

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); }}Copy the code

At this point, the reward process is completed, and the URL class diagram is as follows.

Four,

The policy pattern ensures that we can switch policies dynamically according to business requirements without changing our code logic. The factory pattern helps us to use specific policies rather than the logic involved in creating the object.

With the combination of the two patterns, when we need to enrich the strategy, we simply need to inherit the AbstractRewardStrategy class without any other code logic. This improves the extensibility of the service and achieves high cohesion and low coupling to a certain extent.