My mailbox: <kco1989@qq.com>.
Welcome to reprint. Please indicate the website for reprinting. http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
The code is fully hosted github Students in need download it by themselves
Introduction
The following problems are encountered in the work project.
The company has a product that can be sold to different agents. But different agents have different ways to distribute profits. There are several ways to distribute profits.
For each fixed income of 1 yuan, the agent's income is 1.00
For each return of 0.1%, fill in the agent's return of 0.1%.
If the rate of return is 0.1% and the fixed income is 1 yuan, the agent's income is 0.1%+1.00.
The rate of return per transaction is 0.1%, the top is 3 yuan, and the bottom is 1 yuan, then the agent's income is 1.00-0.1%-3.00.
-
Gradient fraction such as 0.1% < 10 000 < 0.2% < 20 000 < 0.3% < 30 000 < 0.5%.
Less than 10,000 at 0.1% profit
Less than 2000 according to 0.2% profit
Less than 30,000 according to 0.3% profit
More than 30,000 according to 0.5% profit
However, it is found that the company's project code is hard-coded. Hard parsing is not conducive to maintaining that all the rules need to use parameter information in one class.
It's no use saying more. Let's feel the code in the project.
public class AgentShareRule { private Integer profitType; // Distribution type private BigDecimal perFixIncome; //First, the parameters used in four cases private BigDecimal perFixInrate; //Second, third, the parameters used in four cases private BigDecimal safeLine; //The parameters used in the third case private BigDecimal capping; //The parameters used in the third case private BigDecimal ladder1Rate; //The parameters used in the fifth case private BigDecimal ladder1Max;//The parameters used in the fifth case private BigDecimal ladder2Rate;//The parameters used in the fifth case private BigDecimal ladder2Max;//The parameters used in the fifth case private BigDecimal ladder3Rate;//The parameters used in the fifth case private BigDecimal ladder3Max;//The parameters used in the fifth case private BigDecimal ladder4Rate;//The parameters used in the fifth case private BigDecimal ladder4Max;//The parameters used in the fifth case // Omit part of the code and getter,setter method }
If the distribution rules change or new ones are added, the code will not know how to maintain it.
Therefore, based on this situation, I rewrote the profit-sharing rule according to the given rule.
Goals to be achieved after rewriting
Modifying the original rules will not affect other rules
New profit-sharing rules that can be easily added
Rewrite examples
Because the rewriting program is only a test program, so the money involved is double. In practical application, to consider accuracy, it needs to be changed to BigDecimal.
Distribution interface
public interface ProfitRole { double getProfit(double money); }
The profit-sharing interface is very simple, that is, to pass in the amount of profit needed, and then calculate the return of profit.
Distribution type
public enum ProfitType { /** * For each fixed income of 1 yuan, the agent's income is 1.00. */ FIXED_INCOME("^" + ProfitType.realNumber + "$"), /** * If the rate of return is 0.1%, the agent's return is 0.1%. */ FIXED_RATE("^"+ ProfitType.rateNumber +"$"), /** * If the rate of return is 0.1% and the fixed income is 1 yuan, the agent's income is 0.1%+1.00. */ FIXED_RATE_AND_FIXED_INCOME("^"+ ProfitType.rateNumber + "\\+" + ProfitType.realNumber + "$"), /** * The return of each transaction is 0.1%, the top is 3 yuan, and the bottom is 1 yuan, the agent's income is 1.00-0.1%-3.00; */ FIXED_RATE_AND_UPPER_LIMIT("^"+ ProfitType.realNumber + "~" + ProfitType.rateNumber + "~" + ProfitType.realNumber + "$"), /** * Gradient fraction such as 0.1% < 10 000 < 0.2% < 20 000 < 0.3% < 30 000 < 0.5%. * Less than 10,000 at 0.1% profit * Less than 2000 according to 0.2% profit * Less than 30,000 according to 0.3% profit * More than 30,000 according to 0.5% profit */ GRADIENT_RATE("^"+ ProfitType.rateNumber+"(<"+ ProfitType.realNumber+"<"+ ProfitType.rateNumber+")+$"); // Uncaptured Matched Positive Real Number private static final String number = "(?:(?:[1-9]+\\d*)|(?:\\d))(?:\\.\\d+)?"; // Capturing Matched Positive Real Numbers private static final String realNumber = "(" + number + ")"; // Capture matching percentage private static final String rateNumber = realNumber + "%"; public static Pattern getNumberPattern(){ return Pattern.compile(number); } private String pattern; ProfitType(String pattern) { this.pattern = pattern; } public Pattern getPattern() { return Pattern.compile(this.pattern); } }
The splitting type is an enumeration class, and the constructor parameter is a splitting expression pattern.
-
ProfitType contains three regular expressions:
number matches real numbers, but does not capture
realNumber matches the real number and captures it
rateNumber matches percentages, capturing only real numbers, not percentages
Five enumeration values correspond to five profit-sharing rules
FIXED_INCOME: $1 for each fixed income, then fill in the agent income of 1.00
FIXED_RATE: For each return of 0.1%, fill in the agent's return of 0.1%.
FIXED_RATE_AND_FIXED_INCOME: If the rate of return is 0.1% and the fixed income is 1 yuan, the agent's income is 0.1%+1.00.
FIXED_RATE_AND_UPPER_LIMIT: The rate of return per transaction is 0.1%, the top is 3 yuan, the bottom is 1 yuan, and the agent's income is 1.00~0.1%~3.00.
GRADIENT_RATE: Gradient fraction such as 0.1% < 10 000 < 0.2% < 20 000 < 0.3% < 30 000 < 0.5%.
Profit Sharing Realization Class
At present, because there are five profit-sharing rules, each corresponding to an implementation class, there are five profit-sharing implementation classes, which correspond to the five profit-sharing rules of ProfitType one by one.
FIXED_INCOME : FixedIncomeRole
FIXED_RATE
:FixedRateRole
FIXED_RATE_AND_FIXED_INCOME
:FixedRateAndFixedIncomeRole
FIXED_RATE_AND_UPPER_LIMIT
:FixedRateAndUpperLimitRole
GRADIENT_RATE
:GradientRateRole
The implementation class of profit sharing is relatively simple, so I don't post code here. I [github] need it.( https://github.com/kco1989/ma... download
Distribution Factories
Finally, a factory is created to create different profit-sharing implementation classes according to different ProfitType s and profit expressions.
public final class ProfitRoleFactory { public static ProfitRole parseProfitRole(ProfitType type, String expression){ Matcher matcher = type.getPattern().matcher(expression); if (!matcher.matches()){ throw new RuntimeException("Incompatibility in the expression of profit sharing" + type + "Rules."); } switch (type){ case FIXED_INCOME: return new FixedIncomeRole(Double.parseDouble(matcher.group(1))); case FIXED_RATE: return new FixedRateRole(Double.parseDouble(matcher.group(1))); case FIXED_RATE_AND_UPPER_LIMIT: return new FixedRateAndUpperLimitRole(Double.parseDouble(matcher.group(1)), Double.parseDouble(matcher.group(2)), Double.parseDouble(matcher.group(3))); case FIXED_RATE_AND_FIXED_INCOME: return new FixedRateAndFixedIncomeRole(Double.parseDouble(matcher.group(1)), Double.parseDouble(matcher.group(2))); case GRADIENT_RATE: List<Double> rates = new ArrayList<>(); List<Double> limits = new ArrayList<>(); Pattern numberPattern = ProfitType.getNumberPattern(); Matcher numberMatcher = numberPattern.matcher(expression); for (int i = 0;numberMatcher.find();i ++){ if (i % 2 == 0){ rates.add(Double.parseDouble(numberMatcher.group())); }else{ limits.add(Double.parseDouble(numberMatcher.group())); } } return new GradientRateRole(rates, limits); default: //never come here return null; } } public static double getProfit(double money, ProfitType type, String expression){ ProfitRole profitRole = parseProfitRole(type, expression); if (profitRole != null){ return profitRole.getProfit(money); } return 0; } }
Finally, create a test class test
public class TestProfitRole { private static final List<Double> testDate = Arrays.asList(100.0,200.0,300.0,400.0,700.0, 1000.0,2000.0,3000.0,7000.0, 10000.0, 20000.0, 30000.0, 70000.0); @Test public void testFixedIncome(){ for (double data : testDate){ double profit = ProfitRoleFactory.getProfit(data, ProfitType.FIXED_INCOME, "1.00"); Assert.assertEquals(1.00, profit, 0.00001); } } @Test public void testFixedRate(){ for (double data : testDate){ double profit = ProfitRoleFactory.getProfit(data, ProfitType.FIXED_RATE, "0.1%"); Assert.assertEquals(data * 0.1 * 0.01, profit, 0.00001); } } @Test public void testFixedRateAndFixedIncome(){ for (double data : testDate){ double profit = ProfitRoleFactory.getProfit(data, ProfitType.FIXED_RATE_AND_FIXED_INCOME, "0.63%+3.00"); Assert.assertEquals(data * 0.63 * 0.01 + 3.0, profit, 0.00001); } } @Test public void testFixedRateAndUpperLimit(){ for (double data : testDate){ double profit = ProfitRoleFactory.getProfit(data, ProfitType.FIXED_RATE_AND_UPPER_LIMIT, "1.00~0.1%~3.00"); double actual = data * 0.1 * 0.01; if (actual < 1.0){ actual = 1.0; } if (actual > 3.0){ actual = 3.0; } Assert.assertEquals(actual, profit, 0.00001); } } @Test public void testGradientRate(){ for (double data : testDate){ double profit = ProfitRoleFactory.getProfit(data, ProfitType.GRADIENT_RATE, "0.1%<1000<0.2%<5000<0.3%<15000<0.5%"); if (data < 1000){ Assert.assertEquals(data * 0.01 * 0.1, profit, 0.00001); }else if (data < 5000){ Assert.assertEquals(data * 0.01 * 0.2, profit, 0.00001); }else if(data < 15000){ Assert.assertEquals(data * 0.01 * 0.3, profit, 0.00001); }else{ Assert.assertEquals(data * 0.01 * 0.5, profit, 0.00001); } } } }
Epilogue
Now, looking back at our goals, our goals are:
Modifying the original rules will not affect other rules
New profit-sharing rules that can be easily added
Goal 1 is achieved, but Goal 2 is somewhat unsatisfactory. For example, if we want to add a new profit sharing rule, we need to change the ProfitType and ProfitRoleFactory classes at the same time, which do not achieve complete decoupling. But there are some improvements compared with the previous code.
Let's leave this question for you to practice first, and then I'll improve the program when I have time.