MST

星途 面试题库

面试题:TypeScript 组合模式下的依赖管理与可维护性

在一个大型 TypeScript 前端项目中,采用组合模式构建了复杂的业务模块。例如,一个 `Dashboard` 组件由多个不同功能的子组件通过组合方式构成,每个子组件又依赖不同的服务。现在项目面临代码维护成本上升,依赖关系混乱的问题。请提出一套优化方案,使用 TypeScript 的高级特性,如依赖注入、装饰器等,来改善依赖管理,提高代码的可维护性和可测试性,并以关键代码片段示例说明。
21.3万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 依赖注入

依赖注入是一种设计模式,通过将依赖传递给一个组件,而不是在组件内部创建依赖,从而使组件更易于测试和维护。

定义服务接口

// 例如定义一个数据获取服务接口
interface DataFetchingService {
    fetchData(): Promise<any>;
}

实现服务

// 具体的数据获取服务实现
class RealDataFetchingService implements DataFetchingService {
    async fetchData(): Promise<any> {
        // 实际的网络请求等操作
        return Promise.resolve({ data: 'fetched' });
    }
}

使用依赖注入

// Dashboard 子组件依赖数据获取服务
class DashboardSubComponent {
    private dataFetchingService: DataFetchingService;

    constructor(dataFetchingService: DataFetchingService) {
        this.dataFetchingService = dataFetchingService;
    }

    async doSomething() {
        const data = await this.dataFetchingService.fetchData();
        console.log(data);
    }
}

// 使用时,传递具体的服务实例
const realService = new RealDataFetchingService();
const subComponent = new DashboardSubComponent(realService);
subComponent.doSomething();

2. 装饰器

装饰器可以用来简化依赖注入的过程,同时为类或方法添加额外的行为。

安装依赖

首先需要安装 reflect - metadata 库,因为装饰器需要它的支持。

npm install reflect - metadata

定义依赖注入装饰器

import 'reflect - metadata';

const SERVICE_METADATA_KEY = 'injectedService';

// 依赖注入装饰器
function Inject(service: any) {
    return function (target: any, key: string) {
        Reflect.defineMetadata(SERVICE_METADATA_KEY, service, target, key);
    };
}

使用装饰器注入依赖

class DashboardSubComponentWithDecorator {
    @Inject(RealDataFetchingService)
    private dataFetchingService: DataFetchingService;

    async doSomething() {
        const data = await this.dataFetchingService.fetchData();
        console.log(data);
    }
}

// 解析依赖
function resolveDependencies(instance: any) {
    const prototype = Object.getPrototypeOf(instance);
    Object.getOwnPropertyNames(prototype).forEach((key) => {
        const service = Reflect.getMetadata(SERVICE_METADATA_KEY, prototype, key);
        if (service) {
            instance[key] = new service();
        }
    });
}

const subComponentWithDecorator = new DashboardSubComponentWithDecorator();
resolveDependencies(subComponentWithDecorator);
subComponentWithDecorator.doSomething();

3. 模块封装与导出

将服务和组件封装在各自的模块中,通过导出特定的接口和实例,使得依赖关系更加清晰。

服务模块

// data - fetching - service.ts
import { DataFetchingService } from './data - fetching - service.interface';

class RealDataFetchingService implements DataFetchingService {
    async fetchData(): Promise<any> {
        return Promise.resolve({ data: 'fetched' });
    }
}

export { RealDataFetchingService };

组件模块

// dashboard - sub - component.ts
import { DataFetchingService } from './data - fetching - service.interface';
import { RealDataFetchingService } from './data - fetching - service';

class DashboardSubComponent {
    private dataFetchingService: DataFetchingService;

    constructor(dataFetchingService: DataFetchingService = new RealDataFetchingService()) {
        this.dataFetchingService = dataFetchingService;
    }

    async doSomething() {
        const data = await this.dataFetchingService.fetchData();
        console.log(data);
    }
}

export { DashboardSubComponent };

通过以上方法,使用 TypeScript 的依赖注入和装饰器等高级特性,可以有效改善依赖管理,提高代码的可维护性和可测试性。