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

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

考虑到成本和时间等因素,当然不是所有的需求变化都要在软件系统中实现。但是总的说来,软件要适应需求的变化,以保持自己的生命力。
这就产生了一种糟糕的现象:软件产品最初制造出来,是经过精心的设计,具有良好架构的。但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容