【问题标题】:Are interfaces useful in Typescript (architecturally speaking)?接口在 Typescript 中有用吗(从架构上讲)?
【发布时间】:2016-08-10 17:04:24
【问题描述】:

我从 C# 背景来到 Typescript。

在 C# 中,如果我有一个名为 BillingService 的类,并且它具有调用计费服务的方法(可以付款、提供帐户余额等),那么对于需要调用该类的代码部分我会设置一个单独的程序集,其中定义了 IBillingService

我的其余代码将使用依赖注入来获取IBillingService,并且永远不会引用实际实现BillingService 的程序集。这促进了单元测试、松散耦合以及围绕数据隐藏的一些其他好处。

当我第一次开始使用 TypeScript 时,我一直在这样做。但现在我想知道它是否有任何真正的好处。明智地使用公共和私人似乎就足够了。

Typescript 中没有单独的程序集。依赖注入可以注入实际的类,就好像它实现了一个接口一样(至少 Aurelia 的依赖注入可以)。无论如何,JavaScript 单元测试框架似乎都被接口混淆了。

所以,这是我的问题:

对于我上面概述的场景,TypeScript 中是否有接口的用例?如果有,它是什么?

【问题讨论】:

  • 接口对于“好的设计”和完善的合同仍然很重要;如果使用得当,它们会形成在程序构建的其余部分中使用的可靠参考蓝图。它们还允许清晰组合多个合同。但是,Typescript 的 type compatibility 将“需要使用”的部分转移了一些......

标签: typescript interface


【解决方案1】:

与其他一些 OO 语言一样,在 typescript 中也不可能有多重继承,但可以实现多个接口,这是接口适用的一种用例。

另一个原因是,如果您想将不同的实现分布在不同的命名空间/模块中,但又希望它们都实现一组特定的方法:

namespace callbacks {
    export interface Callback<T> {
        getName(): string;
        execute(): T;
    }
}

namespace mynamespace1 {
    export class Callback implements callbacks.Callback<string> {
        public getName(): string {
            return "mynamespace1.Callback";
        }

        public execute(): string {
            return "executed";
        }
    }
}

namespace mynamespace2 {
    export class Callback implements callbacks.Callback<boolean> {
        public getName(): string {
            return "mynamespace2.Callback";
        }

        public execute(): boolean {
            return true;
        }
    }
}

但最好的理由(在我看来)是它允许您将实现类隐藏在一个闭包中,这样就没有人可以直接创建它们,只能通过工厂函数或某些操作:

namespace logging {
    const httpLoggingEndpoint: URL = new URL(...);
    const fileLoggingFilePath: string = "LOG_FILE_PATH";

    export enum LoggerType {
        Console,
        Http,
        File
    }

    export interface Logger {
        log(message: string): void;
    }

    export function getLogger(type: LoggerType): Logger {
        switch (type) {
            case LoggerType.Console:
                return new ConsoleLogger();

            case LoggerType.Http:
                return new HttpLogger();

            case LoggerType.File:
                return new FileLogger();
        }
    }

    class ConsoleLogger implements Logger {
        public log(message: string): void {
            console.log(message);
        }
    }

    class HttpLogger implements Logger {
        public log(message: string): void {
            // make a request to httpLogingEndpoint
        }
    }

    class FileLogger implements Logger {
        public log(message: string): void {
            // log message to the file in fileLoggingFilePath
        }
    }
}

这种方式没有人可以直接实例化记录器,因为没有导出任何实际的类。

关于这个主题的另一点是,在打字稿中,类可以被视为接口:

class Logger {
    public log(message: string) {
        console.log(message);
    }
}

class HttpLogger implements Logger {
    public log(message: string) {
        // log using an http request
    }
}

例如用于mixins,因此实际上我的前两个场景也可以使用类来完成,尽管我的最后一个示例不适用于类,因为这样您就可以实例化基类并通过这样做绕过了不能直接调用不同构造函数的“安全机制”。

【讨论】:

    猜你喜欢
    • 2020-09-13
    • 2012-09-28
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    相关资源
    最近更新 更多