Silverlight数学引擎(3)——重构和Bug修复
这才写了几行代码就要重构啊?不错,子曰:小洞不补大洞一丈五,记住,时间可能会使你丧失重构的勇气。
一:FinderBase.Find()的bug:
public INode Find(string expression) { Match = Regex.Match(expression); INode node = null; if (Match.Success) { node = GenerateNode(expression, Match.Value, Match.Index); } return node; }
对于一般的Finder没有问题,因为只要Match成功了就一定有一个Node返回,但是对于NegationFinder则未必,
所以对于NegetionFinder有可能出现这种情况:如果第一个子式是减法,其后面跟有取反子式,则NegationFinder在匹配到第一个‘-’后立即返回Null,
导致后面的取反节点解析不到。其实这种Bug是不会出现的,因为减法最终会被发现而表达式也会被重构,这会导致NegationFinder又将重新被启用。
以下是修复后的代码:
public INode Find(string expression) { Match = Regex.Match(expression); while (Match.Success) { var node = GenerateNode(expression, Match.Value, Match.Index); if (node != null) return node; Match = Match.NextMatch(); } return null; }
二:IFinder的臭味设计及其引发的Bug:
来看一下上次对IFinder类的重构:
再来看一下上次对IFinder类的重构: //重构前: public interface IFinder { int Priority { get; } Calculator Calculator { get; set; } INode Find(string expression); } //重构后: public interface IFinder { int Priority { get; } Calculator Calculator { set; } INode Find(string expression); void AdjustExpression(ref string expression, ref List<IFinder> finders); }
再来看一下对AdjustExpression的使用,典型的由于不合理的设计导致的Bug:
//此处可能改变了finders的集合,不能用foreach for (int i = finders.Count - 1; i >= 0; i--) finders[i].AdjustExpression(ref expression, ref finders);
天哪,怎么会这样,这是我写出来的吗?,不错,就是你写的!由于需要采用倒序的for循环来AdjustExpression ,导致我们的规则(Finder优先级)被倒置,你想造反啊?
静下心来思索一下面向对象的设计原则吧,不要被直接的懒惰和欲望冲昏了头脑,分析一下我们重构后的IFinder类,是不是变得比以前胖了?(接口应该是功能单一的瘦接口,如果无意间变胖了,你需要静下来反思)。
无话可说,开始减肥吧:
public interface IFinder { int Priority { get; } Calculator Calculator { get; set; } INode Find(string expression); } public interface IExpressionAdjustor { void AdjustExpression(ref string expression, ref List<IFinder> finders); } /// <summary> /// 函数解析器,如Sin(a) /// </summary> public class FunctionFinder : FinderBase, IExpressionAdjustor { //… //重构原始表达式,并生成每个函数的Finder实例 public void AdjustExpression(ref string expression, ref List<IFinder> finders) { //… } }
对女人来说,减肥就意味着新生,重构也一样,看看,一个神奇的新生命IExpressionAdjustor诞生了不是吗?
新生的降临必将导致失态的变革,来对比一下前后对AdjustExpression的使用,Bug已经消失了,正义的降临必将导致邪恶的归隐,是不是很有道理?
//之前: for (int i = finders.Count - 1; i >= 0; i--) finders[i].AdjustExpression(ref expression, ref finders); //之后: var expressionAdjustors = finders.OfType<IExpressionAdjustor>().ToList(); foreach (var adjustor in expressionAdjustors) adjustor.AdjustExpression(ref expression, ref finders);
三:Calculator类的重构:
来看一下 CalculateExpression方法,我们发现里面的逻辑比较杂乱,例如AdjustInputExpression,生成finders等,好像不应该是Calculator该干的事情,应该有Finder来干才对,因为Finder才是表达式的使用者。
public double CalculateExpression(List<IFinder> finders, string expression) { AdjustInputExpression(ref expression); //… finders.ForEach(f => f.Calculator = this); //此处可能改变了finders的集合,不能用foreach for (int i = finders.Count - 1; i >= 0; i--) finders[i].AdjustExpression(ref expression, ref finders); bool findOver = false; while (!findOver) { //… } //… }
从功能方面来分类,我们把一些方法封装起来(主要是静态方法)包括在FinderBase类中,以下是重构后的Calculator类,是不是感觉爽了很多?
public class Calculator { private List<INode> _foundNodes; public List<INode> FoundNodes { get { return _foundNodes; } } public INode RootNode { get { return FoundNodes.Last(); } } public double CalculateExpression(string expression) { _foundNodes = new List<INode>(); FinderBase.FindAllNodes(this, ref expression); if (FoundNodes != null && FoundNodes.Count >= 1) return RootNode.GetValue(); return double.NaN; } internal INode GetNode(string id) { return FoundNodes.FirstOrDefault(n => n.Id == id); } }
好了,重构就到此结束吧。
写了这么多表达式的解析,最终都是为了迎接伟大的未知数——X的入场。现在界面上还只有两个框框和一个Button,连Silverlight的影子都没有见到,别急,马上就要看到了那美丽迷人勾魂的性感曲线了,挖哈哈哈!请关注下一节——X之谜!
浙公网安备 33010602011771号