重构(笔记)——第七章

在对象之间搬移特性

“决定把责任放在哪儿”

搬移函数

一个类有太多的行为,或者如果一个类与另一个类有太多使用而形成的高度耦合,就需要搬移函数。

// 书中的示例
class Account {
  private AccountType _type;
  private int _daysOverdrawn;
  
  double overdraftCharge() {
    if (_type.isPermium()) { // isPermium() 属于 AccountType 的函数
      double result = 10;
      if (_daysOverdrawn > 7) {
        result += (_daysOverdrawn - 7) * 0.85;
      }
      return result;
    } else {
      return _daysOverdrawn * 1.75;
    }
  }
  
  double bankCharge() {
    double result = 4.5;
    if (_daysOverdrawn > 0) {
      result += overdraftCharge();
    }
    return result;
  }
}

// 重构后
class Account {
  private AccountType _type;
  private int _daysOverdrawn;
  
  int getDaysOverdrawn(){
    return _daysOverdrawn;
  }
  
  double bankCharge() {
    double result = 4.5;
    if (_daysOverdrawn > 0) {
      result += _type.overdraftCharge(this);
    }
    return result;
  }
}

class AccountType {
  double overdaftCharge(Account account) {
    if (isPermium()) {
      double result = 10;
      if (account.getDaysOverdrawn() > 7) {
        result += (account.getDaysOverdrawn() - 7) * 0.85;
      }
      return result;
    } else {
      return account.getDaysOverdawn() * 1.75;
    }
  }
}

将函数由一个类迁移到另一个类,在迁移过程中需要注意的问题是所有依赖的方法也必须相应的进行修改。

搬移字段

将一个类的字段由本类迁移到另外一个类中。

// 书中的示例
class Account {
    private AccountType _type;
    private double _interestRate;
    
    double interestForAmount_days (double amount, int days) {
        return _interestRate * amount * days / 365;
    }
}

// 重构后
class AccountType {
    private double _interestRate;
    
    void setInterestRate(double arg){
        _interestRate = arg;
    }
    
    double getInterestRate() {
        return _interestRate;
    }
}

class Account {
    private AccountType _type;
    
    double interestForAmount_days (double amount, int days) {
        return _type.getInterestRate() * amount * days / 365;
    }
}

搬移字段与搬移函数基本类似,只是操作的对象不一样,一个是操作的函数,一个操作的是字段(成员变量)。

提取类

一个类应该是一个清楚的抽象,处理一些明确的责任。

而现状是,在增加一些功能是,基本都放在一个类中,类会越来越大,越来越复杂。而解决这一问题的办法就是将这些本该拆分成多个类的代码提取出来,放入一个新的类中。

// 书中的示例
class person {
    private String _name;
    private String _officeAreaCode;
    private String _officeNumber;
    
    public String getTelephoneNumber() {
        return ("(" + _officeAreaCode + ") " + _officeNumber);
    }
    
    String getOfficeAreaCode() {
        return _officeAreaCode;
    }
    
    void setOfficeAreaCode(String arg) {
        _officeAreaCode = arg;
    }
    
    String getOfficeNumber() {
        return _officeNumber;
    }
    
    void setOfficeNumber(String arg) {
        _officeNumber = arg;
    }
}

// 重构后,书中将其提取出了一个 TelephoneNumber 类
class TelephoneNumber {
    private String _areacode;
    
    String getAreaCode() {
        return _areaCode;
    }
    
    void setAreaCode(String arg) {
        _areaCode = arg;
    }
}

class Person {
    public String getTelephoneNumber() {
        return ("(" + getOfficeAreaCode() +") " + _officeNumber);
    }
    
    Strng getOfficeAreaCode() {
        return _officeTelephone.getAreaCode();
    }
}
// 将原本属于 Person 类中的 _officeAreaCode 和 _officeNumber 封装到 TelphoneNumber 类中

将类内联化

提取类 正好相反,某个类没有做太多事情,将这个类的所有特性搬移到另一个类中,然后移除原类。

// 案例与上一个正好相反,把 TelephoneNumber 类内联到 Person 类中,然后删除 TelephoneNumber 类

思考:某些重构手法正好是两个相反的操作,一个添加,一个移除。如何选择哪个手法才是见功力的,我目前的判断是,看复杂程度,过于复杂就添加,原本有多余的而且就很简单,就删除。

隐藏"委托关系"

“封装”即使不是对象的最关键特征,也是最关键特征之一。

如果某个客户先通过服务对象的字段得到另一个对,然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一委托关系发生变化,客户也得相应进行变化。这时的作法是,可以在服务对象上放置一个简单的委托函数,将委托关系隐藏起来,从而去除这种依赖。

// 书中的示例
class Person {
    Department _department;
    
    public Department getDepartment() {
        return _department;
    }
    
    public void setDepartment(Department arg) {
        _department = arg;
    }
}

class Department {
    private String _chargeCode;
    private Person _manager;
    
    public Department(Person manager) {
        _manager = manager;
    }
    
    public Person getManager() {
        return _manager;
    }
}
// 调用 
manager = john.getDepartment().getManager();

// 重构后 
class Department {
    private String _chargeCode;
    private Person _manager;
    
    public Department(Person manager) {
        _manager = manager;
    }
    
    public Person getManager() {
        return _manager;
    }
}
// 调用 
manager = john.getManager();

移除中间人

当类提供的函数复杂到一定程度时,我们为了隐藏 ”委托关系“ 而添加的函数越来越多时,索性将 ”委托关系“ 暴露出来,供调用方直接调用。

隐藏“委托关系” 正好相反的操作。

// 示例代码与上面相反,去除 Department 类中的 getManager() 方法,直接暴露出 getDepartment() 方法获取中间人,然后调用对应的函数以实现其目的。

引入外加函数

当某个函数需要扩展,而我们无法直接修改这个类时,可以增加函数来实现该功能。

// 书中的示例
Date newStart = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1);

// 重构后
private static Date nextDay(Date arg) {
    return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}

我们在项目中开发代码时,经常会写一些工具类(XxxUtils),其实跟这个重构手法是差不多一个操作。

引入本地扩展

与上面的需求基本一致,我们无法直接修改这个类,但需要在这个类的基础上进行一些扩展。此时我们可以有两种 做法:

  • 扩展出一个子类
  • 增加一个包装类
// 书中的示例
class MfDateSub extends Date {
    public MfDateSub nextDay() {
        ...
    }
    
    public int dayOfYear() {
        ...
    }
}

// 包装类
class MfDateWarp {
    private Date _original;
    
    public MfDateSub(String dateString) {
        _original = new Date(dateString);
    }
    
    public MfDateWarp(Date date) {
        _original = date;
    }
}

参考资料:
[1]: https://book.douban.com/subject/4262627/ "重构-改善既有代码的设计"

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容

  • 《重构》读书笔记 总览 第一部分 第一章从实例程序出发,展示设计的缺陷,对其重构可以了解重构的过程和方法。 第二部...
    白桦叶阅读 2,387评论 2 5
  • 一,重构,第一个案例 这一章作者先用一个影片出租程序的案例,来演示重构的过程 每个Customer顾客可以租多部M...
    高稷阅读 10,798评论 1 19
  • 可以先看【推荐】://www.greatytc.com/p/d6ff54d72afb原文:http://...
    郭某人1阅读 1,837评论 0 0
  • (一)重构原则 1、何谓重构 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低...
    小村医阅读 2,244评论 7 14
  • 最近有人在知乎@我,问:“在部队,受了处分的士官是否还能入党。我告诉他:”很难,不过不是没机会。只要表现好,还是有...
    林阵阅读 1,555评论 0 1