当我使用工厂模式时,我有一个问题:当我有一个新类型时,我需要修改我的工厂类。比如:
animal.Java:
public interface Animal {
void eat();
}
dog.Java:
public class Dog implements Animal {
...
@Override
public void eat(){
System.out.println("eat bones");
}
}
cat.Java
public class Cat implements Animal {
...
@Override
public void eat(){
System.out.println("eat fish");
}
}
现在我有一个Animal界面和两个Animal类,然后我在这里有我的工厂:
public class AnimalFactory {
public static final int Dog = 1;
public static final int Cat = 2;
public static Animal getAnimal(int type) {
switch(type) {
case Dog: return new Dog();
case Cat: return new Cat();
default: return null;
}
}
}
这很简单,但如果我有一个新类型Mouse
,我必须修改我的工厂添加像case Mouse: return new Mouse
的案例声明。
那么,有没有办法增加新类型而不需要修改我的工厂类?
为了使工厂不需要为每个添加/删除的Animal
类型进行更新/修改,您必须提取将Animal
类及其工厂标识符检索到Animal
类本身的逻辑。
概括地说,所有动物子类都应该实现一个返回工厂标识符的方法,在工厂中,您应该从特定包中加载实现此接口的所有类。使用reflections library这个加载类更简单。
但是在编译时实现它既不直接也不安全可靠。
为了不维护工厂,更好的选择是使用依赖注入框架提供的工厂特性。
不再需要工厂类。
使用Spring,Dog可以声明为:
@Component
@Scope("prototype")
public class Dog extends Animal{..}
请注意原型范围,以指示必须在每次注入时创建新实例。
从客户端,您可以注入Animal实例,例如:
@Autowired
private Dog dog;
或者使用明确的工厂Dog dog = ApplicationContext.getBean("dog");
在你的情况下,你的工厂方法目前没有任何复杂的逻辑来确定应该实例化哪个类并返回它的对象 - 所以你不能将动物类型作为整数常量传递,你也可以使用泛型传递java.lang.Class<? extends Animal> animalClass
作为方法参数并创建通过反射使用具体类的相应动物实例(看看Class.newInstance()
)...每次添加新动物类型时,这将使您免于编写其他代码...
简单的例子,您可能想要处理可以在方法实现中抛出的特定异常,而不是仅抛出所有异常...
Class<? extends Animal> animalClass
只允许传递给工厂方法的Class实例作为参数,它们是Animal的子类/接口实现类。
public static Animal getAnimal(Class<? extends Animal> animalClass) throws Exception {
return animalClass.newInstance();
// >=JAVA9: return animalClass.getDeclaredConstructors().newInstance();
}
使用您工厂的客户代码:
Animal animal = AnimalFactory.getAnimal(Dog.class);