1. Example of strategy pattern prototype
To implement a tax calculation strategy, the tax calculation types include internal and external taxes, and new tax types may be added in the future. The initial design class structure is as follows:

class | Duty |
---|---|
TaxStrategy | Tax policy interface |
InterTaxStrategy | In price tax strategy, responsible for calculating in price tax |
OuterTaxStrategy | Extra price tax policy, responsible for calculating extra price tax |
TaxType | Tax type definition, currently there are only in price tax and out of price tax |
TaxStrategyFactory | Tax policy factory obtains different tax policies according to tax type to calculate tax |
Also note here: no matter you are for Java high salary or hobbies, remember: project development experience is always the core. If you don't have the latest Java architecture practical video tutorial and the interview book of large factory, you can go to the small-scale Java architecture to learn. Skirt: seven umbrella bar zero, but no umbrella (Digital homophony) you can find it under conversion. There are many new Java architecture project tutorials in it. You can also communicate with the old driver for advice!
2. Code
2.1. Tax policy code
public interface TaxStrategy {
double calc(long amount);
}
class InterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount * taxRate;
}
}
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount / (1 + taxRate) * taxRate;
}
}
// Definition of tax type
public enum TaxType {
INTER, OUTER
}
Copy code
2.2. Tax policy factory implemented by if statement
// Tax strategy factory
public class TaxStrategyFactory {
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// When adding a new tax type, you need to modify the code and increase the circle complexity
if (taxType == TaxType.INTER) {
return new InterTaxStrategy();
} else if (taxType == TaxType.OUTER) {
return new OuterTaxStrategy();
} else {
throw new Exception("The tax type is not supported.");
}
}
}
Copy code
It can be seen that if you use the if statement to obtain different tax policies, when you add new tax policies, you have to modify the existing code. When there are many tax calculation methods, they are not so good-looking, but also increase the circle complexity.
2.3. Use Map instead of if in the first optimized tax strategy factory
public class MapTaxStrategyFactory {
// Storage tax policy
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
// Register default tax policy
static {
registerTaxStrategy(TaxType.INTER, new InterTaxStrategy());
registerTaxStrategy(TaxType.OUTER, new OuterTaxStrategy());
}
// Provide the tax registration policy interface. External users only need to call this interface to add a new tax policy without modifying the internal code of the policy factory
public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
taxStrategyMap.put(taxType, taxStrategy);
}
// Get the tax policy through map. When adding new tax policy, you don't need to modify the code. It is closed to modification, open to extension and follows the open close principle
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// When adding a new tax type, you need to modify the code and increase the circle complexity
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
}
Copy code
It can be seen that after evolution, IF statements are not available, which reduces cycle complexity. After adding new policies, only the policy registration interface needs to be called, and the code for obtaining tax policies does not need to be modified.
2.4. Automatic registration of secondary optimization strategy
In the above implementation, to register a new tax policy, you must manually call the registration interface of MapTaxStrategyFactory. In this way, for each new tax policy added, you need to modify the existing code, or find an appropriate initialization call point to register the tax policy. How can it perfectly conform to the opening and closing principle, close the modification, and open the extension?
After further optimization, the class structure is as follows:
class | Duty |
---|---|
TaxStrategy | Tax policy interface, providing tax calculation interface, and registering to tax policy factory |
InterTaxStrategy | In price tax strategy, responsible for calculating in price tax |
OuterTaxStrategy | Extra price tax policy, responsible for calculating extra price tax |
TaxType | Tax type definition, currently there are only in price tax and out of price tax |
AutoRegisterTaxStrategyFactory | Tax policy factory obtains different tax policies to calculate tax according to tax type, and provides tax policy registration interface |
Let me look at the changed code:
2.4.1. Tax strategy
public interface TaxStrategy {
double calc(long amount);
// New self registration interface
void register();
}
class InterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount * taxRate;
}
@Override public void register() {
// Register yourself in the policy factory
AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.INTER, this);
}
}
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount / (1 + taxRate) * taxRate;
}
@Override public void register() {
// Register yourself in the policy factory
AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.OUTER, this);
}
}
Copy code
2.4.2. Tax factory
import java.util.*;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
public class AutoRegisterTaxStrategyFactory {
// Storage tax policy
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
static {
// Registration tax strategy
autoRegisterTaxStrategy();
}
// Get tax policy through map. When adding new tax policy, you don't need to modify the code. It is closed to modification, open to extension, and follows the open closed principle
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// When adding a new tax type, you need to modify the code and increase the circle complexity
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
// Provide the tax registration policy interface. External users only need to call this interface to add a new tax policy without modifying the internal code of the policy factory
public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
taxStrategyMap.put(taxType, taxStrategy);
}
// Automatic registration tax policy
private static void autoRegisterTaxStrategy() {
try {
// Find all tax policy subclasses for registration through reflection
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(TaxStrategy.class.getPackage().getName()))
.setScanners(new SubTypesScanner()));
Set<Class<? extends TaxStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(TaxStrategy.class);
if (taxStrategyClassSet != null) {
for (Class<?> clazz: taxStrategyClassSet) {
TaxStrategy taxStrategy = (TaxStrategy)clazz.newInstance();
// Self registration method of calling tax policy
taxStrategy.register();
}
}
} catch (InstantiationException | IllegalAccessException e) {
// Self defined exception handling
e.printStackTrace();
}
}
}
Copy code
Note: the dependency that the reflection tool needs to add in the code is as follows
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
<optional>true</optional>
</dependency>
Copy code
2.4.3. Use
public class DecisionDemo {
public static void main(String[] args) throws Exception {
TaxStrategy taxStrategy = AutoRegisterTaxStrategyFactory.getTaxStrategy(TaxType.INTER);
System.out.println(taxStrategy.calc(100));
}
}
Copy code
At this point, when adding a new tax policy, you do not need to modify the existing tax policy factory code at all. Basically, the principle of opening and closing is perfect. The only thing that needs to be modified is the definition of tax type.
2.5. Three times optimization reduces coupling through annotation (suggested by user Gu Yuanyuan)
The basic idea is to use annotations to explain what type of tax is used in tax strategies, and to automatically register the tax strategy according to annotations in tax strategy factories, without calling the registration interface of tax policy factories in each tax policy. The class structure is as follows:
Let's look at the changing code.
2.5.1. TaxTypeAnnotation.java
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface TaxTypeAnnotation {
TaxType taxType();
}
Copy code
2.5.2. Strategy
The tax policy removes the registration method and adds a taxtype annotation annotation to identify the tax type.
public interface TaxStrategy {
double calc(long amount);
}
@TaxTypeAnnotation(taxType = TaxType.INTER)
class InterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount * taxRate;
}
}
@TaxTypeAnnotation(taxType = TaxType.OUTER)
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // Tax rate obtained
return amount / (1 + taxRate) * taxRate;
}
}
Copy code
2.5.3. Factory
public class AnnotationTaxStrategyFactory {
// Storage tax policy
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
static {
registerTaxStrategy();
}
// Get the tax policy through map. When adding new tax policy, you don't need to modify the code. It is closed to modification, open to extension and follows the open close principle
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// When adding a new tax type, you need to modify the code and increase the circle complexity
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
// Automatic registration tax policy
private static void registerTaxStrategy() {
try {
// Find all tax policy subclasses for registration through reflection
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(TaxStrategy.class.getPackage().getName()))
.setScanners(new SubTypesScanner()));
Set<Class<? extends TaxStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(TaxStrategy.class);
if (taxStrategyClassSet != null) {
for (Class<?> clazz: taxStrategyClassSet) {
// Find the tax type annotation and automatically complete the tax policy registration
if (clazz.isAnnotationPresent(TaxTypeAnnotation.class)) {
TaxTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(TaxTypeAnnotation.class);
TaxType taxType = taxTypeAnnotation.taxType();
taxStrategyMap.put(taxType, (TaxStrategy)clazz.newInstance());
}
}
}
} catch (InstantiationException | IllegalAccessException e) {
// Self defined exception handling
e.printStackTrace();
}
}
}
Copy code
This method reduces the dependence of tax strategy and tax factory, only needs to pay attention to its own algorithm implementation, decoupling is the best.
2.6. Ultimate optimization and refinement of general design mode
In the software system, there are many similar strategies and algorithms, such as different tax calculation strategies, different encryption strategies, different XXX strategies, etc. these strategies can be unified and re abstracted to extract the public policy interface and policy factory class, so that there is no need for each strategy to have a set of code implementation, and it is enough to share a set of code.
This abstract design pattern can be called the self registration strategy pattern, and the actual code will not be written, leaving you to think about it (hint: use generics to abstract).
3. Summary
Note: no matter you are for Java high salary or hobbies, remember: project development experience is always the core. If you don't have the latest Java architecture practical video tutorial and the interview book of large factory, you can go to the small-scale Java architecture to learn. Skirt: seven umbrella bar, zero clothes and zero umbrella (Digital homophony) you can find it under conversion. There are many new Java architecture project tutorials in it. You can also communicate with the old driver for advice!
- Map, instead of if, can improve scalability and reduce cycle complexity.
- The self registration strategy mode meets the opening and closing principle gracefully, which is closed to modification and open to expansion.
- The more familiar you are with the basic features of Java, the better you can think of solutions, such as the annotation features used in this article. Therefore, you should learn more about java basic features at ordinary times. Don't forget to understand new features if you think they are enough. New features must have its advantages, such as solving performance, elegant coding, decoupling, etc.
The text and pictures of this article come from the Internet and my own ideas. They are only for learning and communication. They have no commercial use. The copyright belongs to the original author. If you have any questions, please contact us in time for handling