简体   繁体   English

Typescript命令模式返回类型

[英]Typescript Command pattern return type

Below is command pattern where you have a Command and a Handler for a command, they get added to the CommandBus and then executed there when called upon. 下面是命令模式,其中有命令的Command和Handler,它们被添加到CommandBus ,然后在被调用时执行。 The command has a type in this case <string> that I want to be returned when executing the Command in the CommandBus 该命令在这种情况下具有<string> ,我希望在CommandBus执行Command时返回该类型

The issue is the last line var whatIsThis = CommandBus.execute<string>(new GetStringCommand("Hello, world")); 问题是最后一行var whatIsThis = CommandBus.execute<string>(new GetStringCommand("Hello, world")); more specifically I want to remove form that statement because it should come from the Command . 更具体地说,我想删除form语句,因为它应该来自Command The CommandBus class should know it should return from class GetStringCommand implements Command<string> . CommandBus类应该知道它应该从class GetStringCommand implements Command<string>返回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"));

The solution above is valid and works fine, however it's a terrible solution because it makes refactoring painful, and it makes me have to repeat myself over and over again since I'll use thousands of commands. 上面的解决方案是有效的,并且工作正常,但它是一个糟糕的解决方案,因为它使重构变得痛苦,并且它使我不得不一遍又一遍地重复自己,因为我将使用数千个命令。

Actual example Here's an example of a command handler that saves a schema in MongoDB, the same command could be utilized for creating a schema in MySQL 实际示例以下是在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);
    }
}

From what I understand your handlers are the real commands, while what you call command is simply the params that the handler needs in order to execute. 从我的理解你的处理程序是真正的命令,而你所谓的命令只是处理程序执行所需的参数。

You can replace your "commands" with simple interfaces, and your handlers become the commands. 您可以使用简单的接口替换“命令”,并且处理程序将成为命令。
You can then remove the need for classes for these new commands because you can just pass the execute function as the command. 然后,您可以删除对这些新命令的类的需要,因为您只需将execute函数作为命令传递即可。

Something like this: 像这样的东西:

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" } });

It seems simpler and easier to maintain, but I'm not sure if it fits all your needs. 它似乎更简单,更容易维护,但我不确定它是否符合您的所有需求。


Edit 编辑

It's not as trivial to get the right type from the execute operation, but you have a few options: 从执行操作中获取正确的类型并不是一件容易的事,但是你有几个选择:

(1) Using a return type generics in CommandData : (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) Using any : (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) Declaring the possible signatures for the execute: (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" });

This option isn't very scaleable, but it's still an option. 此选项不是很可扩展,但它仍然是一个选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM