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之谜!

 【源代码和演示地址】

 

posted @ 2012-11-23 09:25  地月银光  阅读(421)  评论(0)    收藏  举报