You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript状态模式实现中的循环依赖问题求助

TypeScript状态模式循环依赖的解决方案

针对你遇到的Context与State抽象类、具体状态类之间的循环依赖问题,以下是几种可行的解决思路:

1. 类型与值导入分离(解决Context与State的双向依赖)

利用TypeScript的import type语法,将类型导入和值导入拆分,避免双向值引用导致的循环依赖:

修改state.ts,仅导入Context的类型而非值:

// state.ts
import type { Context } from "./context"; // 仅导入类型,不触发值加载

export abstract class State {
    protected context: Context;

    public setContext(context: Context) {
        this.context = context;
    }

    public abstract handle1(): void;
    public abstract handle2(): void;
}

此时context.ts导入State的值用于实例化,state.ts仅导入Context的类型用于类型标注,双向循环依赖被打破。

2. 状态注册机制(解决具体状态间的循环依赖)

不让具体状态直接引用彼此,而是通过统一的注册表管理状态类,实现状态切换的解耦,同时适配后续5-10个状态的扩展需求:

第一步:创建状态注册表

// state-registry.ts
import type { State } from "./state";

// 存储状态类的注册表
const stateRegistry = new Map<string, new () => State>();

// 注册状态类
export function registerState(name: string, stateClass: new () => State) {
    stateRegistry.set(name, stateClass);
}

// 根据名称创建状态实例
export function createState(name: string): State {
    const stateClass = stateRegistry.get(name);
    if (!stateClass) throw new Error(`未找到状态:${name}`);
    return new stateClass();
}

第二步:修改具体状态类

// stateA.ts
import { State } from "./state";
import { registerState, createState } from "./state-registry";

export class StateA extends State {
    public handle1(): void {
        console.log('StateA handles request1.');
        console.log('StateA wants to change the state of the context.');
        this.context.transitionTo(createState('StateB'));
    }

    public handle2(): void {
        console.log('StateA handles request2.');
    }
}

// 注册当前状态
registerState('StateA', StateA);
// stateB.ts
import { State } from "./state";
import { registerState, createState } from "./state-registry";

export class StateB extends State {
    public handle1(): void {
        console.log('StateB handles request1.');
    }

    public handle2(): void {
        console.log('StateB handles request2.');
        console.log('StateB wants to change the state of the context.');
        this.context.transitionTo(createState('StateA'));
    }
}

// 注册当前状态
registerState('StateB', StateB);

这种方式下,具体状态类不再互相导入,而是通过注册表间接创建目标状态实例,彻底解决循环依赖,同时新增状态只需注册即可,扩展性更强。

3. 合并核心文件(快速解决方案)

如果项目结构允许,可将Context、State抽象类和核心状态类合并到同一个文件中,避免跨文件的循环导入:

// state-machine.ts
export abstract class State {
    protected context: Context;

    public setContext(context: Context) {
        this.context = context;
    }

    public abstract handle1(): void;
    public abstract handle2(): void;
}

export class Context {
    private state: State;

    constructor(state: State) {
        this.transitionTo(state);
    }

    public transitionTo(state: State): void {
        console.log(`Context: Transition to ${(<any>state).constructor.name}.`);
        this.state = state;
        this.state.setContext(this);
    }

    public request1(): void {
        this.state.handle1();
    }

    public request2(): void {
        this.state.handle2();
    }
}

export class StateA extends State {
    public handle1(): void {
        console.log('StateA handles request1.');
        console.log('StateA wants to change the state of the context.');
        this.context.transitionTo(new StateB());
    }

    public handle2(): void {
        console.log('StateA handles request2.');
    }
}

export class StateB extends State {
    public handle1(): void {
        console.log('StateB handles request1.');
    }

    public handle2(): void {
        console.log('StateB handles request2.');
        console.log('StateB wants to change the state of the context.');
        this.context.transitionTo(new StateA());
    }
}

这种方式简单直接,但状态数量增多时会导致文件体积变大,需根据项目规模权衡使用。

内容的提问来源于stack exchange,提问作者borndeprecated

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:33:10