我们假设我有一个界面
interface I <T extends InternalResult> {
doStuff(): T;
}
以及实现此接口的两个具体类:
class A implements I <InternalAResult> {
doStuff(): InternalAResult {
// implementation
}
}
class B implements I <InternalBResult> {
doStuff(): InternalBResult {
// implementation
}
}
我的客户端不能直接执行doStuff(),但需要实现Executer接口的类的实例,如下所示:
interface Executer <T, R> {
execute(it: T): R;
}
class AExecuter implements Executer <A, AResult> {
execute(it: A): AResult {
let internalResult = it.doStuff();
// ... do something specific with internalResult create result
return result;
}
}
class BExecuter implements Executer <B, BResult> {
execute(it: B): BResult {
let internalResult = it.doStuff();
// ... do something other specific with internalResult create result
return result;
}
}
对于这种情况,我总是使用Java中的访问者模式.我可以创建一个访问者,向它传递两个Executer实例,并使用A和B重载实现visit方法,创建一个无条件解决方案,如下所示:
class Visitor {
private aExecuter: AExecuter;
private bExecuter: BExecuter;
visit(it: A): Result {
return this.aExecuter.execute(it);
}
visit(it: B): Result {
return this.bExecuter.execute(it);
}
}
现在,在TypeScript / JavaScript中没有方法重载这样的东西.但是创建像这样的条件的替代方法是什么?:
if (it instanceof A)
aExecuter.execute(it);
else if (it instanceof B)
bExecuter.execute(it);
注意:我希望A类对AExecuter一无所知.这会增加这两个类之间的耦合,我无法轻易切换AExecutor的实现.
最佳答案 您可以使用typescript
compiler team使用的方法,并有一个区分字段类型的字段,用简单的字符串/数字比较替换instanceof,这可能更便宜(尽管您应该测试您的用例):
enum Types {
A, B
}
interface I <T extends InternalResult> {
readonly type : Types;
doStuff(): T;
}
class A implements I <AResult> {
readonly type = Types.A
doStuff(): AResult {
return new AResult();
}
}
class B implements I <BResult> {
readonly type = Types.B
doStuff(): BResult {
return new BResult();
}
}
class Visitor {
private aExecuter: AExecuter = new AExecuter();
private bExecuter: BExecuter = new BExecuter();
visit(it: A | B): AResult | BResult {
// Since we don't have a default return, we will get a compiler error if we forget a case
switch(it.type){
case Types.A : return this.aExecuter.execute(it); break;
case Types.B : return this.bExecuter.execute(it); break;
}
}
}
另一种需要较少写入(但类型安全性较低)的方法是使用构造函数名作为访问正确方法的键:
class Visitor {
private aExecuter: AExecuter;
private bExecuter: BExecuter;
visit(it: A | B): AResult | BResult {
let visitor: (it: A | B) => AResult | BResult = (this as any)['visit' + it.constructor.name];
if(visitor == null) {
throw "Visitor not found"
}
return visitor.call(this, it)
}
visitA(it: A): AResult {
return this.aExecuter.execute(it);
}
visitB(it: B): BResult {
return this.bExecuter.execute(it);
}
}