引言
在我们写工厂类的时候,可能会根据不同的类型(type)生成不同的对象。但在工厂类初始化的时候,我们需要将某个类型的所有类全部初始化才能达到我们的目的。
举个例子,我们定义了很多动物(Animal),我们需要一个AnimalFactory根据动物类型(type)去构建不同的动物实例。如下代码所示:
我们先构建一个动物基础类型,包含两个方法:
- getType:获取动物类型
- train:训练动物
public interface IAnimal {
/**
* 获取动物种类
* @return
*/
int getType();
/**
* 训练动作
*/
void train();
}
然后我们定义了多个动物实现IAnimal接口
public class TDog implements IAnimal {
/**
* 获取动物种类
*
* @return
*/
@Override
public int getType() {
return 1;
}
/**
* 训练动作
*/
@Override
public void train() {
System.out.println("握手");
}
}
public class TLion implements IAnimal{
/**
* 获取动物种类
*
* @return
*/
@Override
public int getType() {
return 1;
}
/**
* 训练动作
*/
@Override
public void train() {
System.out.println("钻火圈");
}
}
接下来我们定义一个工厂类(TAnimalFactory),用来创建不同的动物
@Service
public class TAnimalFactory {
private static List<Class<? extends IAnimal>> animalLists = Lists.newArrayList();
private static Map<Integer, Class<? extends IAnimal>> animalMaps = Maps.newHashMap();
static {
animalLists.add(TDog.class);
animalLists.add(TLion.class);
}
@PostConstruct
public void init() throws IllegalAccessException, InstantiationException {
for (Class<? extends IAnimal> clazz : animalLists){
IAnimal obj = clazz.newInstance();
animalMaps.put(obj.getType(), clazz);
}
}
/**
* 构建动物类
* @param type
* @return
*/
IAnimal build(int type) throws IllegalAccessException, InstantiationException {
return animalMaps.get(type).newInstance();
}
}
有了工厂类,我们就可以根据动物类型获取不同的动物实例。
存在的问题
上述工厂类可以解决我们大部分问题,但是当我们新增一种动物类型时(Cat),我们不但要创建一个动物类,还需要再工厂类(AnimalFactory)里进行注册
public class TCat implements IAnimal {
/**
* 获取动物种类
*
* @return
*/
@Override
public int getType() {
return 3;
}
/**
* 训练动作
*/
@Override
public void train() {
System.out.println("打滚");
}
}
在工厂类里注册这种类型
static {
animalLists.add(TDog.class);
animalLists.add(TLion.class);
//注册新类型
animalLists.add(TCat.class);
}
如果我们忘记注册了,通过工厂类就无法获取该动物
解决方案(获取基础与IAnimal的所有子类)
我们可以通过Reflections获取继承IAnimal的所有子类,这样我们新增一个动物就无需再进行注册。
如下代码所示:
@Service
public class TAnimalFactory {
private static List<Class<? extends IAnimal>> animalLists = Lists.newArrayList();
private static Map<Integer, Class<? extends IAnimal>> animalMaps = Maps.newHashMap();
static {
//获取该路径下所有类
Reflections reflections = new Reflections("com.lanxing.day.daylearning.animal.newT");
//获取继承了IAnimal的所有类
Set<Class<? extends IAnimal>> classSet = reflections.getSubTypesOf(IAnimal.class);
animalLists.addAll(classSet);
}
@PostConstruct
public void init() throws IllegalAccessException, InstantiationException {
for (Class<? extends IAnimal> clazz : animalLists){
IAnimal obj = clazz.newInstance();
animalMaps.put(obj.getType(), clazz);
}
}
/**
* 构建动物类
* @param type
* @return
*/
IAnimal build(int type) throws IllegalAccessException, InstantiationException {
return animalMaps.get(type).newInstance();
}
}
我们通过Reflections获取某个路径下的所有类,并通过getSubTypesOf方法获取某个类的所有子类信息。这样,后续我们再增加新的动物,只要保证在原先的路径下,就不需要再手动进行注册。
缺陷:
-
Reflections只能扫描某个包下边的所有类,所以所有的子类必须放到同一个路径下
-
getSubTypesOf里的参数必须是所有类都继承的最近层级子类,因为返回的结果里包含了所有该类的子类,不一定是最后一个子类。例如我们还定义了一个抽象类继承IAnimal
public abstract class AbsAnimal implements IAnimal{ void beforeTrain(){ System.out.println("做准备"); } }
那么通过getSubTypesOf获取的返回结果里也包含AbsAnimal类,但其并没有实现getType接口,所以就会报错。
总结
Reflections是基于Java反射的一个工具类,其提供了很多有意思的方法,有兴趣的同学可以研究下https://static.javadoc.io/org.reflections/reflections/0.9.10/org/reflections/Reflections.html