这个问题专门针对C#,但我也对C ++和Java(甚至其他语言,如果他们有一些很酷的东西)的答案感兴趣。
我在继承的“C using C#语法”代码中使用多态替换switch语句。我一直在困惑创建这些对象的最佳方法。我有两种我倾向于使用的后退方法。我想知道是否还有其他可行的替代方案,我应该考虑,或者只是一个健全的检查,我实际上是以合理的方式解决这个问题。
我通常使用的技术:
我应该强烈考虑#3,#4 ......等等吗?
一些细节......请注意,原始设计不是我的,我的时间有限,只要重写/重构整个事情。
以前的伪代码:
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语句的工厂的调用)似乎不是真的修复了这个问题。我只是把开关移到其他地方......这可能很好,因为它不再是我主要逻辑的中间。
答案 0 :(得分:2)
您可能希望将解析器实例化器放在对象本身上,例如,
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
对象,则可能无效。但至少提供有关正确处理程序的信息的责任现在属于parsee,而不是处理程序,这在这一点似乎更正确。
答案 1 :(得分:1)
你可以有一个
public interface IParser<SomeType> : IParser{}
设置structuremap以查找“SomeType”的解析器
似乎Commands与现有代码中的解析器相关,如果您发现它的场景干净,您可能希望保持原样,只需向Parser请求命令。
更新1:我重新阅读原始代码。我认为对于您的场景,如上所述定义IParser可能是最少的变化,它具有适当的GetCommand和SetInput。
命令/输入部分,看起来就像是这样:
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应该发生什么的信息,命令实际上是否随参数变化,因为我不确定该建议是什么因为它。
答案 2 :(得分:0)
我没有看到像你这样的消息处理程序有任何问题。我当然不会使用配置文件方法 - 为什么在编译时可以使所有内容都可用时在调试器外部创建一个配置文件?
答案 3 :(得分:0)
第三种选择是以分散的方式在运行时发现可能的命令。
例如,Spring可以使用所谓的“类路径扫描”,反射和注释在Java中执行此操作 - Spring解析您指定的包中的所有类,选择使用@Controller,@ Resource等注释的类并注册它们就像豆子一样。
Java中的类路径扫描依赖于添加到JAR归档的目录条目(以便Spring可以枚举各种类路径目录的内容)。
我不知道C#,但是应该有一个类似的技术:可能你可以枚举程序集中的类列表,并根据一些标准(命名约定,注释,等等)选择一些类。
现在,考虑到第三种选择,这只是第三种选择。我怀疑它实际上应该在实践中使用。你首先选择(只写一段知道所有类的代码)应该是默认选择,除非你有其他令人信服的理由。
在OOP的分散世界中,每个类都是这个难题的一小部分,必须有一些“集成代码”知道如何将这些部分组合在一起。拥有这样的“全知”类(只要将它们仅限于应用程序级和子系统级集成代码)就没有错。
无论您选择哪种方式(硬编码类中的可能选项,读取配置文件或使用反射来发现选择),它都是相同的故事,并不重要,并且可以随时轻松更改
玩得开心!