• 重构手法之简化函数调用【2】


    返回总目录

    本小节目录

    4Separate Query from Modifier(将查询函数和修改函数分离)

    概要

    某个函数既返回对象状态值,又修改对象状态(副作用)。

    建立两个不同的函数,其中一个负责查询,另一个负责修改。

    动机

    任何有返回值的函数,都不应该有看得到的副作用。如果有一个函数“既有返回值又有副作用”,那么就应该尝试着将查询动作从修改动作中分割出来。

    范例

    有这样一个函数:一旦有人入侵安全系统,它会返回入侵者的姓名,并发送一个警报:

    class Security
    {
    
        void CheckSecurity(string[] people)
        {
            string found = FoundMiscrent(people);
        }
        string FoundMiscrent(string[] people)
        {
            foreach (var person in people)
            {
                if (person == "Don")
                {
                    SendAlert();
                    return "Don";
                }
                if (person == "John")
                {
                    SendAlert();
                    return "John";
                }
            }
            return string.Empty;
        }
    
        void SendAlert()
        {
    
        }
    }

    为了将查询动作和修改动作分开,首先建立一个适当的查询函数,使其与修改函数返回相同的值,但不造成任何副作用。

    string FoundPerson(string[] people)
    {
        foreach (var person in people)
        {
            if (person == "Don")
            {
                return "Don";
            }
            if (person == "John")
            {
                return "John";
            }
        }
        return string.Empty;
    }

    然后,逐一替换原函数内所有的return语句,该调用新建的查询函数:

    string FoundMiscrent(string[] people)
    {
        foreach (var person in people)
        {
            if (person == "Don")
            {
                SendAlert();
                return FoundPerson(people);
            }
            if (person == "John")
            {
                SendAlert();
                return FoundPerson(people);
            }
        }
        return FoundPerson(people);
    }

    现在修改调用者,将原本的单一调用者替换为两个调用:先调用修改函数,然后调用查询函数:

    void CheckSecurity(string[] people)
    {
        FoundMiscrent(people);
        string found = FoundPerson(people);
    }

    替换完毕后,将修改函数的返回值改为void,并修改其函数名称:

    void SendAlert(string[] people)
    {
        foreach (var person in people
        {
            if (person == "Don")
            {
                SendAlert();
                return;
            }
            if (person == "John")
            {
                SendAlert();
                return;
            }
        }
    }

    当然,这种情况下,得到了大量重复代码,因为修改函数之中使用了与查询函数相同的代码。现在对修改函数实施Substitute Algorithm,设法让它再简洁一些:

    void SendAlert(string[] people)
    {
        if (FoundPerson(people) != "")
        {
            SendAlert();
        }
    }

    小结

    如果某个函数只是向你提供一个值,没有任何看得到的副作用,那么你可以任意调用这个函数,也可以把调用动作搬移到函数的其他地方。简而言之,需要操心的事情少多了。

    5Parameterize Method(令函数携带参数)

    概要

    若干函数做了类似的工作,但在函数本体中包含了不同的值。

    建立单一函数,以参数表达那些不同的值

    动机

    有这样两个函数:它们做着类似的工作,但因少数几个值致使行为略有不同。这时,可以将这些各自分离的函数统一起来,并通过参数来处理那些变化的情况,用以简化问题。

    范例

    先看一个简单的例子:

    class Employee
    {
        public double Salary { get; set; }
    
        void TenPercentRaise()
        {
            Salary *= 1.1;
        }
        void FivePercentRaise()
        {
            Salary *= 1.05;
        }
    }

    这段代码可以替换如下:

    class Employee
    {
        public double Salary { get; set; }
    void Raise(double factor) { Salary *= 1 + factor; } }

    再来看一个稍微复杂的例子:

    class Dollars
    {
        public Dollars(double result)
        {
    
        }
    }
    class Charge
    {
        private Dollars BaseCharge()
        {
            double result = Math.Min(LastUsage(), 100) * 0.03;
            if (LastUsage() > 100)
            {
                result += (Math.Min(LastUsage(), 200) - 100) * 0.05;
            }
            if (LastUsage() > 200)
            {
                result += (LastUsage() - 200) * 0.07;
            }
            return new Dollars(result);
        }
    
        private int LastUsage()
        {
            return 201;
        }
    }

    上述代码可以替换如下:

    class Charge
    {
        private Dollars BaseCharge()
        {
            double result = UsageInRange(0, 100) * 0.03;
            result += UsageInRange(100, 200) * 0.05;
            result += UsageInRange(200, Int32.MaxValue) * 0.07;
            return new Dollars(result);
        }
    
        private int UsageInRange(int start, int end)
        {
            if (LastUsage() > start)
            {
                return Math.Min(LastUsage(), end) - start;
            }
            return 0;
        }
        private int LastUsage()
        {
            return 201;
        }
    }

    本项重构的要点在于:以“可将少量数值视为参数”为依据,找出带有重复性的代码。

    小结

    本项重构可以去除重复代码,并提高灵活性,因为可以用这个参数处理更多的变化情况。

    To Be Continued……

  • 相关阅读:
    asp.net程序集冲突解决笔记(未能加载文件或程序集"XXXXXXXXX")
    让Asp.net mvc WebAPI 支持OData协议进行分页查询操作
    jQuery Select 自动选择默认值
    nuget在jenkins上不能自动还原项目依赖包---笔记
    Ubuntu 14.04 server ssh 远程服务遇到的一点事儿
    Unbunt vi 编辑器键盘按键不正确的一次经历与解决方案
    Ubuntu root 密码 sudo passwd
    Visual Studio 2015 下 编译 libpng
    .NET使用Com组件的一点点教训笔记~
    Linux透明大页(Transparent Huge Pages)对ES性能对影响
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7900350.html
Copyright © 2020-2023  润新知