我继承了一个包含一些巨大的switch语句块的项目,其中一些包含多达20个案例。什么是重写这些的好方法?
答案 0 :(得分:16)
为什么要在不同的结构中重写它们?如果您确实有20个案例需要单独处理,那么就可以使用开关/箱子了。维持一个if / then逻辑的大链条。
如果您使用面向对象语言,则多态性是另一种选择。每个子类都将在方法中实现它自己的功能。
答案 1 :(得分:6)
答案 2 :(得分:5)
正如其他人所指出的,它取决于switch语句。但是,在过去,我通过以下方式继续重构switch语句。假设我们有一个像这样的switch语句,有很多重复的代码
switch(x){
case 1:
makeitso("foo");
globalLog += "foo";
case 2:
makeitso("bar");
globalLog += "bar";
case 3:
makeitso("baz");
globalLog += "baz";
...
default:
throw("input error");
}
首先要做的是识别常见的部分(在现实世界中,这可能是 little 更实质)
makeitso([some string]);
globalLog += [some string];
并将其转换为函数
function transformInput(somestring) {
makeitso(somestring);
globalLog += somestring;
}
然后对于每种情况下更改的部分,使用散列或数组;
var transformvalues = ["foo", "bar", "baz"];
从这里我们可以做到这一点:
var tvals = ["foo", "bar", "baz" ... ];
function transformInput(somestring) {
makeitso(somestring);
globalLog += somestring;
}
var tval = tvals[x];
if(tval!==undefined) {
transformInput(tval);
} else {
throw ("invalid input");
}
从switch语句中考虑了tvals,它甚至可以在外部提供,以扩展您可以处理的案例数量。或者你可以动态构建它。但在现实世界中,switch语句通常会有特殊情况。我把它作为读者的练习。
答案 3 :(得分:4)
三条建议(与已经给出的一些建议相呼应):
也许开关没有您想象的那么糟糕。如果案件块很大,那可能会很难看。通过将逻辑提取到方法中来缩短它们。
在OO语言中,正如许多人所指出的那样,多态性可能就是答案。
在函数式语言中,如Javascript,编写一个函数,返回运行任何输入所需的函数。这可能会使用switch语句本身,或者它可能使用查找表。
答案 4 :(得分:2)
您始终可以使用lookup table。
答案 5 :(得分:2)
在switch语句中有20个案例没有错。您可以通过重构整理代码,并至少将案例处理移动到方法/函数中。
答案 6 :(得分:2)
根据switch语句的评估内容,您可能希望使用策略模式对其进行重构。请查看this post,了解使用单独的类替换枚举值上的开关以处理每个函数的示例。
使用具有20个案例的开关可能实际上是它的最佳行动方案。只要它是可读的,每个结果都清楚地传达了动作的内容,就没有必要真正重构它。
答案 7 :(得分:2)
一般情况下,我认为您应该只在需要时进行重构,例如当您想要添加更多功能时,但当前的设计不适用于任务。然后,您应该重构而不添加新功能,然后才添加新功能。
在其他情况下,不要为重构而烦恼。在你需要时这样做,否则可能有更重要的事情要做。
如果你真的需要,那么Visitor Design Pattern是一个常见的交换机案例替换,但你应该注意它确实有缺点。 (即查看www.objectmentor.com/resources/articles/acv.pdf)
答案 8 :(得分:0)
这取决于switch语句的作用。
如果它匹配字符或字符串,比如说在解析器中,并且你没有在代码中的任何地方重复相同的模式集,那么switch语句可能没问题。
如果它与允许值列表匹配(比方说)整数,则可以为每个值创建基类和一组派生类。然后,无论何时生成整数数据,都可以创建派生类的实例,并使用所有switch语句“answers”。
第三种选择是创建一个数据结构,将模式映射到动作(即,使用虚方法的函数或对象)。您可以在此数据结构中查找切换值,并执行相应的操作。
答案 9 :(得分:0)
如果它没有重大错误(不重要,我的意思是它们不会让你把头发拉出来),为什么还要重构呢?不要重构一切。
如果你愿意,你可以将它改为多态,但这将是一个猎枪手术,你可能不得不重构比这个开关块更多的东西。
答案 10 :(得分:-1)
访问https://github.com/Pedram-Ahmadpour/Switch-Case
创建条件的实例,然后通过 Switch()函数将条件传递给条件对象。
int sense = 2;
ConditionSense conditionSense = new ConditionSense();
conditionSense.Switch(sense);
创建条件操作。此界面定义了如何执行条件。
public interface IAction
{
void Do();
}
创建所需案例列表。这些类必须实现条件操作和 ICase ;让它们保持清淡。
public class CaseCry : IAction, ICase<int?>
{
public int? Key { get { return 2; } }
public void Do()
{
Sense.Cry cry = new Sense.Cry();
cry.Act();
}
}
ICase 只包含一个键, Switch()函数用它来导航案例。
public interface ICase<TCase>
{
TCase Key { get; }
}
创建一个条件类,它继承 SwitchCase 通用抽象类。
定义 Switch()功能并导航 Cases 属性以查找匹配案例,然后将其作为条件操作执行。
public class ConditionSense : SwitchCase<int?>
{
public ConditionSense()
{
Cases = new List<ICase<int?>>
{
new CaseSmile(),
new CaseCry()
};
DefaultCases = new List<ICase<int?>> {
new CaseNoSense()
};
}
public void Switch(int? key)
{
IEnumerable<IAction> matches = Cases.Where(p => p.Key.Equals(key))
.Select(p => p as IAction);
if (matches.Count() > 0)
foreach (IAction match in matches)
match.Do();
else
foreach (IAction defaultCase in DefaultCases)
defaultCase.Do();
}
}
微笑,哭泣 ...,可以是巨大的,不要担心它们的大小; 条件操作和 ICase 让它们延迟加载。
public class Sense
{
public class Smile
{
public void Act()
{
Console.WriteLine("I'm smiling :-)");
}
}
public class Cry
{
public void Act()
{
Console.WriteLine("I'm crying :-(");
}
}
public class NoSense
{
public void Act()
{
Console.WriteLine("I've no sense :-|");
}
}
}