功能图与switch语句


21

我正在处理一个处理请求的项目,该请求有两个组件:命令和参数。每个命令的处理程序非常简单(<10行,经常<5)。至少有20条命令,可能会超过50条。

我提出了一些解决方案:

  • 一个大的开关/ if-else命令
  • 命令到功能的映射
  • 命令到静态类/单的映射

每个命令都进行一点错误检查,唯一可以抽象的位是检查为每个命令定义的参数数量。

什么是解决此问题的最佳解决方案,为什么?我也愿意接受我可能错过的任何设计模式。

我为每个提出了以下优点/缺点列表:

开关

  • 优点
    • 将所有命令保持在一个功能中;由于它们很简单,因此使其成为可视化查找表
    • 不需要使用大量只会在一个地方使用的小功能/类来弄乱源代码
  • 缺点
    • 很长
    • 难以以编程方式添加命令(需要使用默认大小写进行链接)

映射命令->功能

  • 优点
    • 一口大小的小块
    • 可以以编程方式添加/删除命令
  • 缺点
    • 如果在线完成,则外观与开关相同
    • 如果没有在线完成,很多功能只能在一个地方使用

映射命令->静态类/单例

  • 优点
    • 可以使用多态来处理简单的错误检查(仅3行,但仍然)
    • 与map类似的好处->功能解决方案
  • 缺点
    • 很多非常小的班级将使项目混乱
    • 实施并非都在同一个地方,因此扫描实施并非易事

额外说明:

我正在用Go编写此代码,但是我认为解决方案不是特定于语言的。我正在寻找一个更通用的解决方案,因为我可能需要用其他语言做一些非常相似的事情。

命令是字符串,但是如果方便的话,我可以轻松地将其映射为数字。函数签名类似于:

Reply Command(List<String> params)

Go具有顶级功能,而我正在考虑的其他平台也具有顶级功能,因此第二个和第三个选项之间的区别。


8
将命令映射到函数,并在运行时从配置中加载它们。使用命令模式。
史蒂文·埃弗斯

3
不要太害怕创建许多小的功能。通常,几个小功能的集合比一个大功能的集合更易于维护。
Bart van Ingen Schenau 2013年

8
在这个充满混乱的世界中,人们在评论中回答问题并在答案中寻求更多信息是怎么回事?
pdr

4
@SteveEvers:如果不需要详尽说明,不管它有多短,它都是一个答案。如果可以,并且您没有时间或其他什么时间,则将其留给其他人回答(总是觉得作弊以写出一个确认已经有六次投票的评论的答案)。就我个人而言,我认为这确实需要阐述,OP确实想知道为什么最佳解决方案是最佳解决方案。
pdr 2013年

1
@pdr-对。我的爱好是将命令映射到函数,但是我是CS设计课程中的初级程序员。我的教授喜欢上很多课,所以至少有两个合法的解决方案。我想知道社区的最爱。
beatgammit 2013年

Answers:


14

这非常适合地图(建议的第二个或第三个解决方案)。我已经使用了数十次,它既简单又有效。我并没有真正区分这些解决方案。重要的一点是,存在一个以函数名称作为键的映射。

我认为,地图方法的主要优点是表格就是数据。 这意味着它可以在运行时传递,增强或以其他方式修改;您也可以轻松编写其他功能,以新颖,令人兴奋的方式解释地图。使用机箱/开关解决方案是不可能的。

我还没有真正体验过您提到的缺点,但是我想提到一个附加的缺点:如果仅字符串名称很重要,则分派很容易,但是如果您需要考虑其他信息才能决定执行哪个函数,它的清洁度要低得多。

也许我从来没有遇到过足够棘手的问题,但是正如其他人所提到的那样,在命令模式和将分发编码为类层次结构方面,我都看不到什么价值。问题的核心是将请求映射到功能。地图是简单,明显且易于测试的。类层次结构需要更多的代码和设计,增加了代码之间的耦合,并可能迫使您提前做出更多决定,以后可能需要更改。我认为命令模式根本不适用。


4

您的问题非常适合Command设计模式。因此,基本上,您将有一个基本Command接口,然后会有多个CommandImpl类来实现该接口。接口本质上只需要一个方法doCommand(Args args)。您可以通过Args类的实例传递参数。这样,您就可以利用多态性的功能,而不是笨拙的if / else语句。同样,这种设计易于扩展。


3

每当我问自己是应该使用switch语句还是OO风格的多态性时,我都会指表达问题。基本上,如果您的数据具有不同的“案例”,并且魔杖支持不同的“操作”(每个案例对每个案例执行的操作有所不同),那么很难创建一个自然可以同时添加新案例和新行为的系统在将来。

如果您使用switch语句(或Visitor模式),那么添加新操作很容易(因为您希望在单个函数中保留所有内容),但是却很难添加新案例(因为您需要返回并编辑旧函数)

相反,如果您使用OO风格的多态性,则很容易添加新用例(仅创建一个新类),但很难向接口添加方法(因为这时您需要返回并编辑一堆类)

在您的情况下,只有一种方法需要支持(处理请求),但有很多可能的情况(每个命令)。由于使添加新用例变得比添加新方法更重要,因此只需为每个不同的命令创建一个单独的类。


顺便说一句,从事物可扩展性的角度来看,无论使用类还是函数都没有太大的区别。如果将我们与switch语句进行比较,那么重要的是如何“分派”事物以及如何以相同的方式分派类和函数。因此,只要使用您语言中更方便的方法即可(并且由于Go具有词法作用域和闭包,所以类和函数之间的区别实际上很小)。

例如,您通常可以使用委派来执行错误检查部分,而不是依赖于继承(我的示例在Javascript中,因为O不知道Go语法,希望您不介意)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

当然,该示例假定每种情况都有一种明智的方法来具有包装函数或父类检查参数。根据情况的不同,将对check_arguments的调用放在命令本身内可能会更简单(因为每个命令可能需要使用不同的参数来调用check函数,这是由于参数数量不同,命令类型不同等)

tl; dr:没有解决所有问题的最佳方法。从“使事物起作用”的角度,着重于以强制重要不变式并保护您免受错误的方式创建抽象。从“面向未来”的角度来看,请记住代码的哪些部分更可能被扩展。


1

我从未使用过go,作为ac#程序员,我可能会沿用以下语言,希望该体系结构适合您的工作。

我将为每个要执行的主要功能创建一个小类/对象,每个人都应该知道其字符串表示形式。随着功能数量的增加,这听起来像您想要的可插入性。请注意,除非您确实需要,否则我不会使用静态变量,因为它们没有太多优势。

然后,我将拥有一个工厂,该工厂知道如何在运行时发现这些类,以将其更改为从不同的程序集等加载。这意味着您不需要在同一项目中全部使用它们。

因此,它也使其更具模块化以进行测试,并应使代码精巧而精巧,以便以后维护。


0

您如何选择命令/功能?

如果有某种“聪明”的方法来选择正确的功能,那就是要走的路-意味着您可以添加新的支持(也许在外部库中)而无需修改核心逻辑。

此外,与庞大的switch语句相比,单独测试单个功能要容易得多。

最后,仅在一个地方使用-您可能会发现,一旦达到50,就可以重用不同功能的不同位?


这些命令是唯一的字符串。如果需要,我可以将它们映射为整数。
beatgammit

0

我不知道Go的工作原理,但是我在ActionScript中使用的体系结构是具有一个双向链接列表,该列表充当责任链。每个链接都有一个defineResponsibility函数(我将其实现为回调,但是如果可以更好地满足您的需要,则可以单独编写每个链接)。如果一个链接确定它有责任,它将调用meetResponsibility(再次是一个回调),这将终止该链。如果没有责任,它将把请求传递到链中的下一个链接。

只需在现有链的链接之间(或末尾)添加新链接,即可轻松添加和删除不同的用例。

这类似于您对功能图的想法,但与之稍有不同。令人高兴的是,您只是传递了请求,而无需执行任何其他操作即可完成请求。不利的一面是,如果您需要返回一个值,它将无法正常工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.