泛型在代码设计中的思考
简介
最近查看了Hibernate Validator泛型的使用和认知有了新的理解,这里结合自己的理解做一个总结。
JAVA 的类型
┌────┐
│Type│
└────┘
▲
│
┌────────────┬────────┴─────────┬───────────────┬──────────────┐
│ │ │ │ │
┌─────┐┌─────────────────┐┌────────────────┐┌────────────┐ ┌────────────┐
│Class││ParameterizedType││GenericArrayType││WildcardType│ │TypeVariable│
└─────┘└─────────────────┘└────────────────┘└────────────┘ └────────────┘
以上是目前java的类系统结构,我们接触最多的是Class类型,但是有了泛型后光只有Class类型是不够的所以java又进一步多抽象出了以下几种类型
-
ParameterizedType
带泛型的参数比如 List
-
GenericArrayType
泛型数据比如 T[]
-
WildcardType
通配符 如List<? extends Number>里的
?
-
TypeVariable
类型变量,例如 List中的
T
泛型小结
- 不能是基本类型,例如:
int
; - 不能获取带泛型类型的
Class
,例如:Pair<String>.class
; - 不能判断带泛型类型的类型,例如:
x instanceof Pair<String>
; - 不能实例化
T
类型,例如:new T()
。
- 子类可以获取父类的泛型类型
<T>
。
关于在设计中的思考
工厂模式
最早在做一系列策略的时候会使用工厂模式,根据入参来在工厂中做if
和else
的判断如下
public Strategy getStrategy(int type){
if(type==1){
return new Strategy1();
}else if (type==2){
return new Strategy2();
}
return new DefaultStrategy();
}
Strategy
是一个接口,而Strategy1
,Strategy2
, DefaultStrategy
是Strategy
的实现类
这样if
和else
并没有消失而是放在了工厂方法里,而不是放在业务中,这样方便维护一些,在简单的业务条件中这是一个不错的实现,因为够简单,一看就懂,但是随着业务复杂度增加,即使我们抽象出了工厂类或者工厂方法,但是随着if
和else
的增多,代码也逐渐出现了坏味道,于是又出现了责任链模式。
责任链模式
其也是Spring中广泛使用的设计模式,其使用遍历的方式间接代替了if
和else
,其代码通常会定义一个Handler和一个Invoker和一个Commond
interface Handler{
void handle();
boolean support(Commond commond);
}
interface Commond{
}
class Invoker(){
private List<Handler> repository;
void invoke(Commond commond){
for (Handler handler:repository){
if(handler.support(commond)){
handler.handle();
}
}
}
}
其核心逻辑就定义一个处理器Handler里面包含一个support
方法,该方法去判断这个处理器能处理什么样的Commond
命令,然后使用者只需要使用Invoker
,传入对应的Commnd就可以完成整个责任链的调用。这里及时业务再复杂我们也只需要上面一个if
就可以解决,
这是一个典型的空间换复杂度的做法,毕竟遍历肯定比if和else耗时,但是还是那句话,究竟多慢才是慢?只要在合理客接受范围内我觉得时间换复杂度就是一种血赚的行为,何况上面这种做法在List不大时也很快
泛型模式
其实并没有这种模式,这只是我查看Hibernate Validator源码后自己瞎起的,我们利用泛型小结中的最后一点子类可以获取父类的泛型类型<T>
。 可以设计出一种基于反射的模式来完成业务的解耦,而不是采用编码的方式(责任链模式中的support方法)话不多说上代码
/**
* @author Liush
* @description 用户请求抽象类
* @date 2021/11/23 17:44
**/
public interface Command {
}
/**
* @author Liush
* @description 用户请求实现类
* @date 2021/11/24 9:12
**/
public class Command1 implements Command {
}
/**
* @author Liush
* @description 策略类
* @date 2021/11/23 17:36
**/
public abstract class Strategy<T> {
void handle(T t) {
}
}
/**
* @author Liush
* @description 策略实现类
* @date 2021/11/23 17:38
**/
public class Strategy1 extends Strategy<Command1> {
@Override
public void handle(Command1 command1) {
System.out.println("do something");
}
}
/**
* @author Liush
* @description 使用示例
* @date 2021/11/23 17:43
**/
public class GenericHandle {
public HashMap<String,Strategy> repository;
public GenericHandle(){
//初始化泛型策略,这部分后续可采用配置或者注解去实现策略的注入,读取策略类的泛型,存入缓存仓库
repository=new HashMap<>();
Type type =Strategy1.class.getGenericSuperclass();
if(type instanceof ParameterizedType){
ParameterizedType parameterizedType=(ParameterizedType)type;
String key=parameterizedType.getActualTypeArguments()[0].getTypeName();
repository.put(key,new Strategy1());
}
}
//根据用户传来的命令Command,执行不同的策略
public void handle(Command command){
Strategy strategy=repository.get(command.getClass().getName());
strategy.handle(command);
}
//使用实例
public static void main(String[] args) {
GenericHandle genericHandle=new GenericHandle();
Command1 command=new Command1();
genericHandle.handle(command);
}
}
总结
其实采用泛型和责任链的区别就是,责任链还是依靠编码(主要依靠support方法),而采用泛型的话则是更多依赖反射,这种方式各有千秋,采用责任链代码可读性更强,而采用反射则是代码更加灵活。