我正在研究一个动物园的例子,它有几种不同类型的动物。用户输入“添加老虎”等命令,然后将Tiger添加到动物园中。
在我的命令解释器类中,我有一些这样的代码:
String animalName...
Animal newAnimal;
if (animalName.equals("tiger"))
newAnimal = new Tiger();
else if (animalName.equals("elephant"))
newAnimal = new Elephant();
这里的问题是当一种新的动物被添加到程序中时,这段代码也必须改变。我想通过继承Animal来添加新动物而不改变现有类中的任何内容。
用户在其命令中给出的名称不一定与动物的类名相同(例如,“添加孟加拉虎”会添加BengalTiger对象)。
如果可能的话,我宁愿避免使用反射。
这是最终代码:
private static String getClassName(String name) {
char ch;
int i;
boolean upper = true;
StringBuilder s = new StringBuilder("animals.");
for (i=0; i<name.length(); i++) {
ch = name.charAt(i);
if (ch!=' ') {
if (upper)
s.append(Character.toUpperCase(ch));
else
s.append(Character.toLowerCase(ch));
upper = false;
} else
upper = true;
}
return s.toString();
}
@Override
public Animal addAnimal(String s) {
try {
Animal animal = (Animal)Class.forName(getClassName(s)).newInstance();
addAnimal(animal);
return animal;
} catch (InstantiationException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (ClassCastException e) {
throw new IllegalArgumentException("There are no animals called like this");
}
}
答案 0 :(得分:3)
您应该使用动态类加载:Class.forName(name)
,例如:
String animal = ... // e.g. elephant
String className = "com.mycompany.animals." + animal.substring(0, 1).toUpperCase() + animal.subString(1);
Animal obj = (Animal)Class.forName(className);
答案 1 :(得分:2)
您需要使用Class.forName(X)才能实现此目的。 Class.forName将帮助您将实际类加载为字符串值。请参阅http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#forName(java.lang.String)。
答案 2 :(得分:1)
Object animalObj = Class.forName(animalString);
但它必须是确切的名称(具有相同的大写字母)。
答案 3 :(得分:1)
为了完整起见,这里是基于AbstractFactory模式的相当冗长的解决方案。你需要继承两个接口而不是一个(动物类型的工厂和动物类型本身),但是,嘿,没有反思!
interface Animal {
String whoAmI();
}
class Tiger implements Animal {
@Override
public String whoAmI() {
return "Tiger";
}
}
interface AnimalFactory {
Animal create();
}
abstract class AbstractAnimalFactory implements AnimalFactory {
protected AbstractAnimalFactory(String animalName, FactoriesRegistry registry) {
registry.register(animalName, this);
}
}
class TigersFactory extends AbstractAnimalFactory {
TigersFactory(FactoriesRegistry registry) {
super("tiger", registry);
}
@Override
public Animal create() {
return new Tiger();
}
}
class FactoriesRegistry {
HashMap<String, AnimalFactory> byAnimalName = new HashMap<String, AnimalFactory>();
Animal produce(String id) {
AnimalFactory factory = byAnimalName.get(id);
if (factory != null)
return factory.create();
else
throw new IllegalArgumentException("Unknown animal " + id);
}
public void register(String animalName, AnimalFactory factory) {
byAnimalName.put(animalName, factory);
}
}
public class SOSample {
public static void main(String[] args) {
FactoriesRegistry registry = new FactoriesRegistry();
TigersFactory tigersFactory = new TigersFactory(registry);
System.out.println(registry.produce("tiger").whoAmI());
}
}
答案 4 :(得分:0)
反思不是像@Victor Sorokin所提到的这个问题的唯一解决方案。 但它是最简单/最干净的代码!
反思解决方案:
public interface Animal {
// put your contract here
}
public class AnimalFactory {
public static final AnimalFactory INSTANCE = new AnimalFactory();
private Map<String, Class<? extends Animal>> animalTypeMap;
private AnimalFactory() {};
public Animal createAnimal(String key) {
Animal animal = null;
if (animalTypeMap != null) {
Class<? extends Animal> animalType = animalTypeMap.get(key);
if (animalType != null) {
try {
animal = animalType.newInstance();
} catch (Throwable error) {
throw new RuntimeException("Unable to create animal with key='" + key + "'", error);
}
}
}
return animal;
}
public Map<String, Class<? extends Animal>> getAnimalTypeMap() {
if (animalTypeMap == null) {
animalTypeMap = new HashMap<String, Class<? extends Animal>>();
}
return animalTypeMap;
}
public void setAnimalTypeMap(Map<String, Class<? extends Animal>> animalTypeMap) {
this.animalTypeMap = animalTypeMap;
}
public void addAnimalType(String key, Class<? extends Animal> animalType) {
getAnimalTypeMap().put(key, animalType);
}
public void removeAnimalType(String key, Class<? extends Animal> animalType) {
if (animalTypeMap != null) {
animalTypeMap.remove(key);
}
}
}
然后,你就这样使用它:
Animal myAnimal = AnimalFactory.INSTANCE.createAnimal("myAnimalKey");
使用此解决方案,您只需要使用您需要的动物类型填写该地图! 另一个解决方案,它也是一个干净/良好的解决方案,但非常冗长......