考虑一个多人格斗游戏,客户端通过向服务器发送一个包含客户端刚刚选择的战士名称的字符串来选择战士。
考虑服务器中的这个“战士”类结构:
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 :)
}
这是处理这次遭遇的正确方式吗?有没有更好的办法?我记得有一种已知的模式可以处理此类遭遇,但不记得具体是哪一种。
如果您有想法,请告诉我!
最佳答案
使用工厂,解决方案可能如下所示:
interface FighterFactory {
Fighter create();
}
保留一个Map<String, FighterFactory>
外观中的变量。每个Fighter
type 必须在此映射中使用其 Factory 注册。
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());
要实例化一个新的 Fighter,请访问 Fighter-Map 并在其结果上执行创建。
FighterFactory factory = fighters.get(nameFromClient);
// null handling, if nameFromClient is not a Fighter type;
return factory.create();
这种设计比反射有一些优势:
- 它是安全的
- create 方法可以做的不仅仅是构造函数
以及单一工厂方法(一个方法处理所有 Fighter 类型):
- 如果创建方法变得比您对代码的更改位于 Fighter 类而不是工厂方法更复杂(例如,创建实例的参数更多)。
以及枚举方法:
- 使用的枚举的唯一特性是迭代它的值。保留一个 Map 的值代替迭代与 Map 查找
- 关于如何构建战斗机的信息位于战斗机附近,而不是在冠军枚举中
- 您甚至可以决定 FighterFactories 是顶级类,这是枚举内部类工厂无法做到的。
关于java - 从字符串初始化子类对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32086139/