Typescript命令模式返回类型

时间:2016-08-10 08:13:25

标签: typescript command-pattern

下面是命令模式,其中有命令的Command和Handler,它们被添加到CommandBus,然后在被调用时执行。该命令在这种情况下具有<string>类型,我希望在CommandBus

中执行命令时返回该类型

问题是最后一行var whatIsThis = CommandBus.execute<string>(new GetStringCommand("Hello, world"));更具体地说,我想删除该语句,因为它应该来自CommandCommandBus类应该知道它应该从class GetStringCommand implements Command<string>返回。

interface Command<T> {
    name:string;
}

class GetStringCommand implements Command<string> {
    public str:string;
    public name:string = "GetStringCommand";
    constructor(str:string){
        this.str = str;
    }
}

interface CommandHandler<T> {
    execute(command:Command<T>): T;
}

class GetStringHandler implements CommandHandler<string> {
    execute(command:GetStringCommand):string {
        return command.str;
    }
}

interface CommandRegistry {
    [x:string]: CommandHandler<any>
}

class CommandBus {
    private static actions:CommandRegistry = {};

    static add(name:string, command:CommandHandler<any>) {
        CommandBus.actions[name] = command;
    }

    static execute<T>(command:Command<T>) : T {
        return CommandBus.actions[command.name].execute(command);
    }
}

CommandBus.add("GetStringCommand", new GetStringHandler());

var whatIsThis = CommandBus.execute<string>(new GetStringCommand("Hello, world"));

上面的解决方案是有效的并且工作正常,但这是一个糟糕的解决方案,因为它使重构变得痛苦,并且它使我不得不一遍又一遍地重复自己,因为我将使用数千个命令。

实际示例 这是一个在MongoDB中保存模式的命令处理程序的示例,可以使用相同的命令在MySQL中创建模式

export class SaveSchemaCommandHandler implements CommandHandlerBase<void> {
    execute(command:SaveSchemaCommand) {
        var db = MongoConnection.db;
        var collectionOptions = { autoIndexID: true };

        db.createCollection(command.schema.getName(), collectionOptions);
    }
}

1 个答案:

答案 0 :(得分:3)

据我所知,你的处理程序是真正的命令,而你所谓的命令只是处理程序执行所需的参数。

您可以使用简单的界面替换“命令”,并且您的处理程序将成为命令 然后,您可以删除对这些新命令的类的需要,因为您只需将execute函数作为命令传递。

这样的事情:

interface CommandData {}

interface GetStringCommandData extends CommandData {
    value: string;
}

interface SaveSchemaCommandData extends CommandData {
    schema: { name: string };
}

type Command<In extends CommandData, Out> = (data: In) => Out;

interface CommandRegistry {
    [x: string]: Command<CommandData, any>;
}

class CommandBus {
    private static actions:CommandRegistry = {};

    static add(name: string, command: Command<CommandData, any>) {
        CommandBus.actions[name] = command;
    }

    static execute<T>(name: string, data: CommandData) : T {
        return CommandBus.actions[name](data);
    }
}

CommandBus.add("GetStringCommand", (data: GetStringCommandData) => data.value);
CommandBus.add("SaveSchemaCommand", (data: SaveSchemaCommandData) => {
    let db = MongoConnection.db;
    let collectionOptions = { autoIndexID: true };

    db.createCollection(data.schema.name, collectionOptions);
});

CommandBus.execute("GetStringCommand", { value: "my string" });
CommandBus.execute("SaveSchemaCommand", { schema: { name: "mySchema" } });

它似乎更简单,更容易维护,但我不确定它是否符合您的所有需求。

修改

从执行操作中获取正确的类型并不是一件容易的事,但是你有几个选择:

(1)在CommandData中使用返回类型泛型:

interface CommandData<T> {}

interface GetStringCommandData extends CommandData<string> {
    value: string;
}

class CommandBus {
    ...

    static execute<T>(name: string, data: CommandData<T>): T {
        return CommandBus.actions[name](data);
    }
}

let str: string = CommandBus.execute("GetStringCommand", { value: "my string" } as CommandData<string>);

(2)使用any

class CommandBus {
    ...

    static execute(name: string, data: CommandData): any {
        return CommandBus.actions[name](data);
    }
}

let str: string = CommandBus.execute("GetStringCommand", { value: "my string" });

(3)声明执行的可能签名:

class CommandBus {
    ...

    static execute(name: "GetStringCommand", data: GetStringCommandData): string;
    static execute(name: "SaveSchemaCommand", data: SaveSchemaCommandData): void;
    static execute(name: string, data: CommandData): any;
    static execute(name: string, data: CommandData): any {
        return CommandBus.actions[name](data);
    }
}

let str: string = CommandBus.execute("GetStringCommand", { value: "my string" });

此选项不是非常可扩展的,但它仍然是一个选项。