相关阅读:java
JAVA编程思想(一)经过依赖注入增长扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
JAVA基础(三)ClassLoader实现热加载
Java并发编程入门(十一)限流场景和Spring限流器实现
HikariPool源码(二)设计思想借鉴
人在职场(一)IT大厂生存法则算法
如今要实现一个算税策略,税计算类型有价内税和价外税,未来可能会增长新的税类型,初始设计类结构以下:编程
类 | 职责 |
---|---|
TaxStrategy | 税策略接口 |
InterTaxStrategy | 价内税策略,负责计算价内税 |
OuterTaxStrategy | 价外税策略,负责计算价外税 |
TaxType | 税类型定义,当前只有价内税和价外税 |
TaxStrategyFactory | 税策略工厂,根据税类型获取不一样的税策略来算税 |
public interface TaxStrategy {
double calc(long amount);
}
class InterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // 获取税率
return amount * taxRate;
}
}
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // 获取税率
return amount / (1 + taxRate) * taxRate;
}
}
// 税类型定义
public enum TaxType {
INTER, OUTER
}
复制代码
// 税策略工厂
public class TaxStrategyFactory {
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// 当增长新的税类型时,须要修改代码,同时会增长圈复杂度
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.");
}
}
}
复制代码
能够看到,若是经过if语句来获取不一样的税策略,当增长新的税策略时就不得不修改已有代码,当算税方法不少时,就不那么好看,同时也增长了圈复杂度。设计模式
public class MapTaxStrategyFactory {
// 存储税策略
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
// 注册默认税策略
static {
registerTaxStrategy(TaxType.INTER, new InterTaxStrategy());
registerTaxStrategy(TaxType.OUTER, new OuterTaxStrategy());
}
// 提供税注册策略接口,外部只须要调用此接口接口新增税策略,而无需修改策略工厂内部代码
public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
taxStrategyMap.put(taxType, taxStrategy);
}
// 经过map获取税策略,当增长新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// 当增长新的税类型时,须要修改代码,同时增长圈复杂度
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
}
复制代码
能够看到,进化后IF语句没有了,减小了圈复杂度,增长新的策略后只需调用策略注册接口就好,不须要修改获取税策略的代码。并发
在上面的实现中,要注册新的税策略,必须手动调用MapTaxStrategyFactory的注册接口,这样,每新增长一个税策略都须要修改已有代码,或者要找到一个合适的初始化调用点,去注册税策略,如何能完美的符合开闭原则,对修改关闭,对扩展开放呢?dom
再次优化后,类结构以下:ide
类 | 职责 |
---|---|
TaxStrategy | 税策略接口,提供算税接口,同时自注册到税策略工厂中 |
InterTaxStrategy | 价内税策略,负责计算价内税 |
OuterTaxStrategy | 价外税策略,负责计算价外税 |
TaxType | 税类型定义,当前只有价内税和价外税 |
AutoRegisterTaxStrategyFactory | 税策略工厂,根据税类型获取不一样的税策略来算税,同时提供税策略注册接口 |
下面我看变化后的代码:工具
public interface TaxStrategy {
double calc(long amount);
// 新增自注册接口
void register();
}
class InterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // 获取税率
return amount * taxRate;
}
@Override public void register() {
// 本身注册到策略工厂中
AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.INTER, this);
}
}
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // 获取税率
return amount / (1 + taxRate) * taxRate;
}
@Override public void register() {
// 本身注册到策略工厂中
AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.OUTER, this);
}
}
复制代码
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 {
// 存储税策略
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
static {
// 注册税策略
autoRegisterTaxStrategy();
}
// 经过map获取税策略,当增长新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// 当增长新的税类型时,须要修改代码,同时增长圈复杂度
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
// 提供税注册策略接口,外部只须要调用此接口接口新增税策略,而无需修改策略工厂内部代码
public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
taxStrategyMap.put(taxType, taxStrategy);
}
// 自动注册税策略
private static void autoRegisterTaxStrategy() {
try {
// 经过反射找到全部的税策略子类进行注册
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();
// 调用税策略的自注册方法
taxStrategy.register();
}
}
} catch (InstantiationException | IllegalAccessException e) {
// 自行定义异常处理
e.printStackTrace();
}
}
}
复制代码
注:代码中反射工具须要添加的依赖以下.post
<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>
复制代码
public class DecisionDemo {
public static void main(String[] args) throws Exception {
TaxStrategy taxStrategy = AutoRegisterTaxStrategyFactory.getTaxStrategy(TaxType.INTER);
System.out.println(taxStrategy.calc(100));
}
}
复制代码
至此,当添加新的税策略时,就彻底不须要修改已有的税策略工厂代码,基本完美作到开闭原则,惟一须要修改的是税类型定义。性能
基本思路是在税策略上使用注解说明是哪一种税类型,在税策略工厂中自动根据注解完成税策略注册,无需在每一个税策略中调用税策略工厂的注册接口。其类结构图以下:
下面看看变化的代码。
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface TaxTypeAnnotation {
TaxType taxType();
}
复制代码
税策略去掉了注册方法,添加TaxTypeAnnotation注解来识别是哪一种税类型。
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; // 获取税率
return amount * taxRate;
}
}
@TaxTypeAnnotation(taxType = TaxType.OUTER)
class OuterTaxStrategy implements TaxStrategy {
@Override public double calc(long amount) {
final double taxRate = 0.2; // 获取税率
return amount / (1 + taxRate) * taxRate;
}
}
复制代码
public class AnnotationTaxStrategyFactory {
// 存储税策略
static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
static {
registerTaxStrategy();
}
// 经过map获取税策略,当增长新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
// 当增长新的税类型时,须要修改代码,同时增长圈复杂度
if (taxStrategyMap.containsKey(taxType)) {
return taxStrategyMap.get(taxType);
} else {
throw new Exception("The tax type is not supported.");
}
}
// 自动注册税策略
private static void registerTaxStrategy() {
try {
// 经过反射找到全部的税策略子类进行注册
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) {
// 找到税类型注解,自动完成税策略注册
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) {
// 自行定义异常处理
e.printStackTrace();
}
}
}
复制代码
此方式减小了税策略和税工厂的依赖,只须要关注本身的算法实现,解耦作得最好。
在软件系统中,相似如上的策略算法会不少,不一样的算税策略,不一样的加密策略,不一样的XXX策略等等,这些能够统一再次抽象,提取出公共的策略接口,策略工厂类,这样就不须要每种策略都有一套代码实现,共用一套代码足矣。
这个抽象出来的设计模式能够称为自注册策略模式,实际代码就不写了,留给你们自行思考完成(提示:使用泛型来抽象)。
- Map替代if实现策略选择,可提升扩展性,减小圈复杂度。
- 自注册策略模式优雅的知足了开闭原则,对修改封闭,对扩展开放。
- 越熟悉Java基础特性越能想到更好的方案,例如在文中使用的注解特性。因此平时应多学习JAVA基础特性,不要以为已经够用就不去了解新特性,新特性出来必定有它的优势,好比解决性能,优雅编码,解耦等等。
end.