读书笔记 -《重构-改善既有代码的设计》Part3

文章目录


上一节 读书笔记 -《重构-改善既有代码的设计》Part2
下一节 读书笔记 -《重构-改善既有代码的设计》Part4

读书笔记 -《重构-改善既有代码的设计》Part3

重新组织函数

Extract Method

处理好临时变量

Inline Method

你有一个临时变量,只被一个简单的表达式赋值一次,而它妨碍了其他重构手法。

将所有对该变量的一你用动作,替换为对它赋值的那个表达式自身。

  • 检查函数,确定它不具备多态性
  • 找出这个函数的所有被调用点
  • 将这个函数的所有被调用点替换为函数本体
  • 编译、测试
  • 删除该函数的定义
int getRating() {
    return (moreThanFiveLateDeliveries()) ? 2 : 1;
}

boolean moreThanFiveLateDeliveries() {
    return _numberOfLateDeliveries > 5;
}

=>

int getRating() {
    return (_numberOfLateDeliveries > 5) ? 2 : 1;
}

Inline Temp - 内联临时变量

将所有对该变量的引用动作,替换为对它赋值的那个表达是自身

double basePrice = anOrder.basePrice();
return (basePrice > 1000);

=>

return anOrder.basePrice() > 1000;

Replace Temp with Query

往往你的程序会以一个临时变量保存某一表达式的运算结果。

将这个表达式提炼到一个独立函数中。

double basePrice = _quantity * _itemPrice;
if (basePrice > 1000) {
    return basePrice * 0.95;
} else {
    return basePrice * 0.98;
}

=>

double basePrice() {
    return _quantity * _itemPrice;
}
......
if (basePrice() > 1000) {
	......   
} else {
    ......
}
  • 找出只被赋值依次的临时变量

  • 将该临时变量生命为 final

  • 编译 – 看看是否只被赋值一次

  • 将 ”对该临时变量赋值“ 的语句提炼成函数

  • 编译、测试

  • 在该临时变量身上实施 Inline Temp

Example

double getPrice() {
    int basePrice = _quantity * _itemPrice;
    double discountFactor;
    if (basePrice > 1000) {
        discountFactor = 0.95;
    } else {
        discountFactor = 0.98;
    }
    return basePrice * discountFactor;
}

=>
int basePrice() {
    return _quantity * _itemPrice;
}

double getDiscountFactor() {
    if (basePrice() > 1000) {
        return 0.95;
    } else {
        return 0.98;
    }
}

double getPrice() {
    return basePrice() * getDiscounterFactor();
}

Introduce Explaining Variable 引入解释性变量

顾名思义,解释性变量的最大好处就是可读性增强。

if (!platform.toUpperCase().indexOf("MAC") > -1 &&
    browser.toUpperCase().indexOf("IE") > -1 &&
    wasInitialized() && resize > 0) {
    ......
}

=>
    
final boolean isMacOs = platform.toUpperCase().indexOf("MAC");
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE");
final boolean wasResized = resize > 0;

if (!isMacOs && isIEBrowser && wasInitialized() && wasResized) {
    ......
}

当然我们也能 Extract Method,提炼函数

boolean isMacOs() {
    return platform.toUpperCase().indexOf("MAC");
}

boolean isIEBrowser() {
    return browser.toUpperCase().indexOf("IE");
}

boolean wasResized() {
    return resize > 0;
}

if (!isMacOs() && isIEBrowser() && wasInitialized() && wasResized()) {
    ......
}

Split Temporary Variable 分解临时变量

你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。

针对每次赋值,创造一个独立、对应的临时变量

double temp = 2 * (_height + _width);
System.out.println(temp);
temp = _height * _width;
System.out.println(temp);

=>

final double perimeter = 2 * (_height + _width);
System.out.println(perimeter);
final double area = _height * _width;
System.out.println(area);

Remove Assignments to Parameters 移除对参数的赋值

对一个参数进行赋值,以一个临时变量取代该参数的位置

在 java 中,不要对参数进行赋值。

int discout (int inputVal, int quantity, int yearToData) {
	if (inputVal > 50) {
        inputVal -= 2;
    }
    ......
}

=>

int discout (int inputVal, int quantity, int yearToData) {
	int input = inputVal;
    if (inputVal > 50) {
        input -= 2;
    }
    ......
}

java 中参数可以标示为 final

Replace Method with Method Object 以函数对象取代函数

一个大型函数,其中对局部变量的使用使你无法采用 Extract Method

将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段,然后可以在同一个对象中将这个大型函数分解为多个小型函数

int gamma(int inputVal, int quantity, int yearToDate) {
    int importantValue1 = (inputVal * quantity) + delta();
    int importantValue2 = (inputVal * yearToDate) + 100;
    if ((yearToDate - importantValue1) > 100) {
        importantValue2 -= 20;
    }
    int importantValue3 = importantValue2 * 7;
    return importantValue3 - 2 * importantValue
}

=>

class Gamma {
    private int inputVal;
    private int quantity;
    private int yearToDate;
    ...
    int compute() {
        ......
    }
}

Substitute Algorithm 替换算法

把某个算法替换为另一个更清晰的算法,将函数本体替换为另一个算法

String findName(String[] names) {
    for (String name : names) {
        if (name.equals("X")) {
            return "X-Man1";
        }
        
        if (name.equals("Y")) {
            return "Y-Man2";
        }
    }
}

=>

String findName(String[] names) {
	List<String> lst = Arrays.asList(new String[] {"X-Man1", "Y-Man2"});
	for (String name : names) {
    	if (-1 != lst.indexOf(name)) {
        	return lst.get([lst.indexOf(name)]);  // 此处会被编译器优化
    	}
	}
}

在对象之间搬移特性

即使你是一个经验丰富的程序员,一开始设计对象时也会出现错误,需要重构去优化。

Move Method

如果一个类有太多行为,或如果一个类与另一个类有太多交互而高度耦合,就要考虑这些行为能否被搬移了。

  • 检查源类中被源函数所使用的一切特性,考虑它们是否该被搬移

  • 检查源类的子类和超类,看看是否有该函数的其它声明

    如果出现其他声明,你或许无法进行搬移,除非目标类也同样表现出多态性

Move Field

对于一个字段,其所驻类之外的另一个类中有更多函数使用了它,就该考虑搬移字段

Extract Class

某个类做了应该由多个类做的事,应该分别建立单一任务的类来分担这些事情,职责明确,更易维护。

Inline Class 将类内联化

Inline Class 正好与 Extract Class 相反,如果一个类不在承担足够责任,可以用 Inline Class 重构代码。

Hide Delegate 隐藏委托关系

class Person {
    Department _department;
    
    public Department getDepartment() {
        return _department;
    }
    
    public void setDepartment(Department department) {
        _department = department;
    }
}

class Department {
    private String _chargeCode;
    private Person _manager;
    
    public Department(Person manager) {
        _manager = manager;
    }
    
    public Person getManager() {
        return _manager;
    }
}
......
manager = somebody.getDepartment().getManager();

上面的代码就把 Manager 的从属关系暴露出去了。为隐藏 Department,在 Person 中建立一个简单的委托函数

public Person getManager() {
    return _department.getManager();
}

用户调用代码变成了

manager = somebody.getManager();

Remove Middle Man 移除中间人

某个类做了过多的简单委托动作,让客户直接调用被委托的类。

过多的 Hide Delegate 也会有麻烦,很难界定什么时候该减少这类封装。如果代码在发展过程中开始变得不适用,就该到重构的时候了。

Introduce Foreign Method 引入外加函数

需要为提供服务的类增加一个函数,但你无法修改这个类

在客户类中建立一个函数,并以第一参数形式传入一个服务类实例

Date newDate == new Date(pre.getYear(), pre.getMonth(), pre.getDate() + 1);

=>

public static Date nextDate(Date pre) {
    return new Date(pre.getYear(), pre.getMonth(), pre.getDate() + 1);
}

Date newDate = nextDate(pre);

Introduce Local Extension 引入本地扩展

你需要为服务类提供一些额外函数,但你无法修改这个类。

建立一个新类,使它包含这些额外函数。让这个扩展成为源类的子类或包装类。

拓展类

class MyDate extends Date {
    public MyDate(String dateString) {
        super(dateString);
    }
    
    public Date MyDateNext() {
        return new Date(this.getYear(), this.getMonth(), this.getDate() + 1);
    }
    
    ......
}

重新组织数据

Self Encapsulate Field 自封装字段

如果直接访问一个字段,与字段的耦合关系就会随时间过去越来越明显。

为这个字段建立 getter/setter,并且之一这些函数访问字段

间接访问变量,可以通过重写函数改变获取数据的方式;管理数据也更加灵活,比如添加打印或添加延时。

Replace Data Value with Object 以对象取代数据值

你有一个数据项,需要与其他数据和行为一起使用才有意义,将数据项变成对象

Change Value to Reference 将值对象改为引用对象

从一个类衍生除许多彼此相等的示例,希望将它们替换为同一个对象

在许多系统中,你都可以对对象做一个有用的分类:引用对象值对象

  • 要在引用对象和值对象之间做出选择并不容易,使用 Replace Constructor with Factory Method

  • 决定由什么对象负责提供访问新对象的途径

  • 决定这些引用对象是预先创建还是动态创建

  • 修改工厂函数,令他返回引用对象

Change Reference to Value 将引用对象改为值对象

如果引用对象变得难以使用,可能要考虑把它们改为值对象

关键是检查是否不可变,如果不是不可变的,我们不能用本项重构。

Replace Array with Object 以对象取代数组

用对象取代数组,往往更易扩展和阅读。

Duplicate Observed Data 复制“被监视数据”

数据的同步常与 GUI 一起工作

在分层设计中,用户界面、数据层、逻辑层是分离的,怎么同步用户界面和数据层,是这个重构方法关注点之一。

Change Unidirectional Association to Bidirectional 将单向关联改为双向关联

开发开始时,一个类只是引用了另一个类的某些特性,但随着开发进行,可能会有反向的依赖

一般的在被引用类中增加一个字段,用以保存反向指针

Change Bidirectional Association to Unidirectional 将双向关联改为单向关联

两个类之间有双向关系,但是现在一个类不需要另一个类的特性了,要去除不必要的关联

注意:不但要检查直接访问点,也要检查调用这些直接访问点的函数

Replace Magic Number with Symbolic Constant 以字面常量取代魔法数字

存在一个带有特殊含义的字面数值,那么可以创建一个常量,以它的含义命名这个常量,用该常量替换它

Encapsulate Field 封装字段

类中有一个 public 字段,应声明为 private, 然后提供相应的访问函数

Encapsulate Collection 封装集合

– 是常见的重构方式

如果 Courses 属于 Person,那么 Person.Courses.add => Person.addCourse(course)

实际上这样的修改很少情况下会导致 Person 变得臃肿,更好的封装了 Person 的 Courses,不对外开放

Replace Record with Data Class 以数据类取代记录

  • 新建一个类表示记录数据

  • 对于记录中的每一项数据,在新建的类中建立对应的一个 private 字段,并提供相应的取值/设值函数

Replace Subclass with Fields 以字段取代子类

子类中只存在常量相关函数,没有足够的存在价值,可以去掉

Replace Type Code with Class 以类取代类型码

类型码和枚举值可能经常出现,虽然可读性不错,如果把那样的数值换成一个类,编译器可以对这个类进行类型校验,现在枚举类(Java 1.5 发行版本)也是由于类型校验不严谨而产生的

class XXType extends Enum {
    
}

Replace Type Code with Subclass 以子类取代类型码

你有一个不可变的类型码,他会影响类的行为,以子类取代这个类型码。

一个不同的类型码代表一个子类,那么可以这么做

比如 Person => Student, Teacher

Replace Type Code with State/Strategy 以 State/Strategy 取代类型码

如果一个类型码无法通过继承体系消除,那么可以考虑以状态对象取代类型码

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
项目描述:建立购物小商城平台. 实现了前台页面系统。 技术描述:通过Spring 主框架来管理Struts2和Hibernate 框架搭建的电商小平台,用MySQL数据库并创建了表有用户表,订单表,商品表,商品分类表,商品内容表,购物车表等来存储数据。用到hibernate….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看rEADME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值