控制圈复杂度的9种重构技术

作者: Rance935 | 来源:发表于2017-03-02 21:55 被阅读387次

    一个软件总是为解决某种特定的需求而产生,时代在发展,客户的业务也在发生变化。有的需求相对稳定一些,有的需求变化的比较剧烈,还有的需求已经消失了,或者转化成了别的需求。在这种情况下,软件必须相应的改变。

    考虑到成本和时间等因素,当然不是所有的需求变化都要在软件系统中实现。但是总的说来,软件要适应需求的变化,以保持自己的生命力。
    这就产生了一种糟糕的现象:软件产品最初制造出来,是经过精心的设计,具有良好架构的。但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。bug越来越多,越来越难维护,新的需求越来越难实现,软件的架构对新的需求渐渐的失去支持能力,而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候。

    重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后,使用重构的方式,不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的结构,使系统对于需求的变更始终具有较强的适应能力。

    重构可以降低项目的耦合度,使项目更加模块化,有利于项目的开发效率和后期的维护。让项目主框架突出鲜明,给人一种思路清晰,一目了然的感觉,其实重构是对框架的一种维护。

    那么说到重构,可能我们普遍认为就是改改框架、使用一些最新流行的框架或者技术等等,我们到底要从哪些方面来对项目进行重构呢?下面我们进入主题:控制圈复杂度的9种重构技术

    • 提炼函数

    例子:如果某段代码可以被组织在一起并独立出来

    void printOwing(double previousAmount)
     {
        Enumeration e = _orders.elements();
        double outstanding = previousAmount * 1.2;
        
          // 打印大标题
        System.out.println ("**************************");
        System.out.println ("***** Customer Owes ******");
        System.out.println ("**************************");
    
        // 计算未完成的订单数量
        while (e.hasMoreElements()) 
        {
            Order each = (Order) e.nextElement();
            outstanding += each.getAmount();
        }
    
        //打印明细
        System.out.println ("name:" + _name);
        System.out.println ("amount" + outstanding);
    }
    

    将这段代码放进一个独立函数中,并让函数名称解释该函数的用途

    void printOwing(double previousAmount) 
    {
        printBanner();
        double outstanding = getOutstanding(previousAmount * 1.2);
        printDetails(outstanding);
    }
    
    void printBanner() //打印大标题
    {
        System.out.println ("**************************");
        System.out.println ("***** Customer Owes ******");
        System.out.println ("**************************");
    }
    
    // 计算未完成的订单数量
    double getOutstanding(double initialValue) 
    {
        double result = initialValue;
        Enumeration e = _orders.elements();
        while (e.hasMoreElements()) 
        {
            Order each = (Order) e.nextElement();
            result += each.getAmount();
        }
        return result;
    }
    
    void printDetails (double outstanding) //打印明细
     {
        System.out.println ("name:" + _name);
        System.out.println ("amount" + outstanding);
    }
    
    • Substitute Algorithm(替换你的算法)

    例子:
    把当前算法重构成更清晰的算法

    String foundPerson(String[] people)
    {
        for (int i = 0; i < people.length; i++) 
        {
            if (people[i].equals ("Don"))
                return "Don";
            if (people[i].equals ("John"))
                return "John";
            if (people[i].equals ("Kent"))
                return "Kent";
        }
        return "";
    }
    

    重构成更清晰的算法

    String foundPerson(String[] people)
    {
        List candidates = Arrays.asList(new     
                String[]{"Don", "John","Kent"});
        for (int i=0; i<people.length; i++)
            if (candidates.contains(people[i]))
                return people[i];
        return "";
    }
    
    • Decompose Conditional(分解条件式)

    例子:你有一个复杂的条件语句

    if (date.before (SUMMER_START) || date.after(SUMMER_END))
        charge = quantity * _winterRate + _winterServiceCharge;
    else 
        charge = quantity * _summerRate;
    

    从if、then、else三个段落中分别提炼出独立函数

    if (notSummer(date))
        charge = winterCharge(quantity);
    else 
        charge = summerCharge (quantity);
    
    • Consolidate Conditional Expression(合并条件式)

    例子:你有一系列条件判断,都得到相同结果

    double disabilityAmount() 
    {
        if (_seniority < 2) return 0;
        if (_monthsDisabled > 12) return 0;
        if (_isPartTime) return 0;
        // compute the disability amount
    

    将这些判断合并为一个条件式,并将这个条件式提炼成为一个独立函数,函数名自注释

    double disabilityAmount() 
    {
        if (isNotEligableForDisability()) return 0;
        // compute the disability amount
    
    • Consolidate Duplicate Conditional Fragments(合并重复的条件片断)

    例子:在条件式的每个分支上有着相同的一段代码。

    if (isSpecialDeal()) 
        {
            total = price * 0.95;
            send();
        }
    else 
        {
            total = price * 0.98;
            send();
        }
    

    将这段重复代码搬移到条件式之外,避免用拷贝粘贴的方式写重复代码

    if (isSpecialDeal())
        total = price * 0.95;
    else
        total = price * 0.98;
    send();
    
    • Remove Control Flag(移除控制标记)

    例子:当前代码使用标记变量,可读性差,容易出错

    void checkSecurity(String[] people) {
        boolean found = false;
        for (int i = 0; i < people.length; i++) {
            if (! found) {
                if (people[i].equals ("Don")){
                    sendAlert();
                    found = true;
                }
                if (people[i].equals ("John")){
                       sendAlert();
                       found = true;
                }
            }
        }
    }
    

    以break和return取代标记变量

    void checkSecurity(String[] people) {
        for (int i = 0; i < people.length; i++) {     
            if (people[i].equals ("Don")){
                sendAlert();
                break;
            }
            if (people[i].equals ("John")){
                sendAlert();
                break;
            }
        }
    }
    
    • Separate Query from Modifier(将查询函数和修改函数分离)

    例子:某个函数既返回对象状态值,又修改对象状态,建立两个不同的函数,其中一个负责查询,另一个负责修改




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

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

    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);
    }
    

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

    Dollars baseCharge() 
    {
        double result = usageInRange(0, 100) * 0.03;
        result += usageInRange (100,200) * 0.05;
        result += usageInRange (200, Integer.MAX_VALUE) * 0.07;
        return new Dollars (result);
    }
    
    int usageInRange(int start, int end) 
    {
        if (lastUsage() > start) 
            return Math.min(lastUsage(),end) -start;
        else 
            return 0;
    }
    

    相关文章

      网友评论

        本文标题:控制圈复杂度的9种重构技术

        本文链接:https://www.haomeiwen.com/subject/dzwkgttx.html