javascript - TypeScript 中访问者模式的替代方案(避免 instanceof 条件)

标签 javascript typescript

假设我有一个接口(interface)

interface I <T extends InternalResult> {
    doStuff(): T;
}

和实现这个接口(interface)的两个具体类:

class A implements I <InternalAResult> {
    doStuff(): InternalAResult {
        // implementation
    }
}

class B implements I <InternalBResult> {
    doStuff(): InternalBResult {
        // implementation
    }
}

我的客户端不能直接执行 doStuff(),但需要一个实现 Executer 接口(interface)的类的实例,例如其中一个:

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 中的 Visitor 模式。我可以创建一个访问者,将两个 Executer 实例传递给它并使用 AB 实现 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);
    }
}

关于javascript - TypeScript 中访问者模式的替代方案(避免 instanceof 条件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51168935/

相关文章:

javascript - 使用 Set 从数组中删除重复项,保留最高的 id

node.js - Typescript 不喜欢我的变量名 "⛄",我无法抑制错误

typescript - 如何使用 aws cdk 将 SQS 队列订阅到 SNS 主题?

typescript - 我想在nestjs中实现自定义缓存

php - 使用 mySQL 数据、ajax、PHP post 更新 insideHTML 文本

javascript - 是否可以使用正则表达式匹配字符串的一半或使用匹配组的一半?

javascript - Windows 边栏环境中的代码分析

javascript - 多级选择栏

javascript - 很奇怪!调用 window.location 或 location.replace 会重定向到该页面,然后再次返回!

javascript - 解决 typescript 错误的最佳方法 - 类型上不存在属性