考虑一个多人格斗游戏,客户通过向服务器发送包含客户刚刚选择的战斗机名称的字符串来选择战斗机。
考虑这个"战士"服务器中的类结构:
Fighter
Foo extends Fighter
Bar extends Fighter
Qux extends Fighter
例如,客户端发送了此"Bar"
字符串,如何根据此字符串启动子类?
我这样做的方式是enum
,如此:
public enum Champion {
Foo {
@Override
public Fighter getNew() {
return new Foo();
}
},
Bar {
@Override
public Fighter getNew() {
return new Bar();
}
},
Qux {
@Override
public Fighter getNew() {
return new Qux();
}
};
public abstract Fighter getNew();
public static boolean contains(String fighter) {
for (Champion c : Champion.values()) {
if (c.name().equals(fighter)) {
return true;
}
}
return false;
}
}
然后我得到一个像这样的对象:
public Fighter getFighter(String fighterName) {
if(Champion.contains(fighterName)) {
return Champion.valueOf(fighterName).getNew();
} else {
return null;
} // For example sake only :)
}
这是处理这次遭遇的正确方法吗?有没有更好的办法?我记得有一种已知的模式来处理这种遭遇,但不记得究竟是哪种 如果您有任何想法,请告诉我们!
答案 0 :(得分:3)
使用工厂,解决方案可能如下所示:
interface FighterFactory {
Fighter create();
}
在立面中保留Map<String, FighterFactory>
变量。每个Fighter
类型都必须在此地图中注册其工厂。
private Map<String, FighterFactory> fighters;
public void register(String name, FighterFactory factory) {
fighters.put(name, factory);
}
在每个Fighter
类型中嵌入内部类Factory
,例如
class Foo extends Fighter {
//code for Foo
public static class Factory implements FighterFactory {
public Foo create() {
return new Foo();
}
}
}
// and in your Facade
register("Foo", new Foo.Factory());
要实例化一个新的战斗机,请进入战斗机地图并对其结果执行创建。
FighterFactory factory = fighters.get(nameFromClient);
// null handling, if nameFromClient is not a Fighter type;
return factory.create();
这种设计比反思有一些优势:
并通过单一工厂方法(一种方法处理所有战斗机类型):
以及enum方法:
答案 1 :(得分:1)
如果java 8是一个选项,您可以使用MapString, Supplier<Fighter>>
:
Map<String, Supplier<Fighter>> factories = new HashMap<>();
factories.put("foo", Foo::new);
factories.put("bar", Bar::new);
factories.put("qux", Qux::new);
然后,您可以让一个供应商使用给定的字符串,并要求它创建专门的Fighter
实例:
String s = "bar";
Supplier<Fighter> supplier = factories.get(s);
if (supplier != null) {
Fighter fighter = supplier.get();
System.out.println(fighter.getClass().getSimpleName()); // Bar
} else {
// Handle illegal string
}
答案 2 :(得分:0)
我从最简单的方法开始。
public Fighter getFighter(String fighterName) {
if("Foo".equals(fighterName))
return new Foo();
if("Bar".equals(fighterName))
return new Bar();
...
return null;
}
然后我可能会将一些逻辑移到子类中。
public Fighter getFighter(String fighterName) {
Fighter result = null;
if((result = Foo.createFromString(fighterName))!=null)
return result;
if((result = Bar.createFromString(fighterName))!=null)
return result;
...
return null;
}
您可以从这里获得很多指示,您可以将构造界面粘贴到Map
或List
。例如。
public registerFighterType(IFighterConstructor constructor) {
fighterTypes.add(constructor);
}
public Fighter getFighter(String fighterName) {
for(IFighterConstructor constructor : fighterTypes) {
Fighter result = constructor.createFromString(fighterName);
if(result!=null) return result;
}
return null;
}
答案 3 :(得分:0)
尝试
MyClass c = Class.forName("MyClass").newInstance();
看起来很丑陋,可能需要在那里某处施放,但它应该有用。