代码之家  ›  专栏  ›  技术社区  ›  James Schek

逻辑现在是多态性而不是开关,但是构造呢?

  •  2
  • James Schek  · 技术社区  · 15 年前

    我用继承的“C使用C#语法”代码中的多态性替换switch语句。我一直在思考创建这些对象的最佳方法。我倾向于使用两种后退方法。我想知道是否有其他可行的替代方案,我应该考虑,或只是一个理智的检查,我实际上正在以一种合理的方式进行这件事。

    1. 使用无所不知的方法/类。这个类要么填充数据结构(很可能是映射),要么使用switch语句动态构造。
    2. 使用一个盲哑类,该类使用配置文件和反射来创建实例/委托/工厂/等的映射。然后以类似于上面的方式使用映射。

    有三、四个。。。等等,我应该 强烈地 考虑


    一些细节。。。请注意,最初的设计不是我的,我的时间有限,可以重写/重构整个设计。

    public string[] HandleMessage(object input) {
    
       object parser = null;
       string command = null;
       if(input is XmlMessage) {
          parser = new XmlMessageParser();
          ((XmlMessageParser)parser).setInput(input);
          command = ((XmlMessageParser)parser).getCommand();
       } else if(input is NameValuePairMessage) {
          parser = new NameValuePairMessageParser();
          ((NameValuePairMessageParser)parser).setInput(input);
          command = ((XmlMessageParser)parser).getCommand();
       } else if(...) {
          //blah blah blah
       }
    
       string[] result = new string[3];
       switch(command) {
          case "Add":
              result = Utility.AddData(parser);
              break;
          case "Modify":
              result = Utility.ModifyData(parser);
              break;
          case ... //blah blah
              break;
       }
       return result;
    }
    

    我计划(在对其他对象进行多次重构之后)将其替换为:

    public ResultStruct HandleMessage(IParserInput input) {
       IParser parser = this.GetParser(input.Type);       //either Type or a property
       Map<string,string> parameters = parser.Parse(input);
       ICommand command = this.GetCommand(parameters);  //in future, may need multiple params 
       return command.Execute(parameters);              //to figure out which object to return.
    }
    

    问题是GetParser和GetCommand的实现应该是什么?

    将switch语句放在那里(或调用由switch语句组成的工厂)看起来不像这样 解决了这个问题。我只是把开关移到别的地方。。。这可能是好的,因为它不再在我的主要逻辑的中间。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Jon Limjap    15 年前

    您可能希望将解析器实例化器放在对象本身上,例如。,

    public interface IParserInput
    {
        ...
        IParser GetParser()
        ICommand GetCommand()
    }
    

    任何参数 GetParser 理论上,需求应该由对象提供。

    将会发生的是,对象本身将返回这些,而您的代码将发生以下情况:

    public ResultStruct HandleMessage(IParserInput input) 
    {
        IParser parser = input.GetParser();
        Map<string,string> parameters = parser.Parse(input);
        ICommand command = input.GetCommand();
        return command.Execute(parameters);
    }
    

    现在这个解决方案并不完美。如果您无权访问 IParserInput

        2
  •  1
  •   eglasius    15 年前

    你可以有一个

    public interface IParser<SomeType> : IParser{}
    

    并设置structuremap以查找“SomeType”的解析器

    更新1:

    命令/输入项将沿以下线条显示:

        public string[] HandleMessage<MessageType>(MessageType input) {
           var parser = StructureMap.GetInstance<IParser<MessageType>>();
           parser.SetInput(input);
           var command = parser.GetCommand();
           //do something about the rest
       }
    

    事实上,您的实现让我觉得旧代码,即使没有if和switch,也有问题。你能提供更多关于在你的实现中GetCommand应该发生什么的信息吗?这个命令是否会随着参数的变化而变化,因为我不确定该建议什么。

        3
  •  0
  •   Beep beep    15 年前

        4
  •  0
  •   Andrey Tarantsov    15 年前

    第三种选择是以分散的方式在运行时发现可能的命令。

    例如,Spring可以在Java中使用所谓的类路径扫描、反射和注释来实现这一点,Spring解析您指定的包中的所有类,选择用@Controller、@Resource等注释的类,并将它们注册为bean。

    我不知道C#,但应该有一种类似的技术:可能您可以枚举程序集中的类列表,并根据某些条件(命名约定、注释等)选择其中的一些类。

    现在,为了考虑第三个选择,这只是第三个选择。我怀疑它是否真的应该在实践中使用。您的第一个选择(只需编写一段了解所有类的代码)应该是默认选择,除非您有令人信服的理由这样做。

    在分散的OOP世界中,每个类都是拼图的一小部分,必须有一些集成代码知道如何将这些部分组合在一起。拥有这些无所不知的类并没有错(只要您将它们限制在应用程序级和子系统级集成代码中)。

    无论您选择哪种方式(硬编码类中可能的选择、读取配置文件或使用反射来发现选择),都是一样的,并不重要,并且可以随时更改。

    玩得高兴