重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)
重构手法16:Introduce Foreign Method (引入外加函数)
你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
动机:这种事情发生了太多次了,你正在使用一个类,它真的很好,为你提供了需要的所有服务。而后,你又需要一项新服务,这个类却无法供应。于是你开始咒骂“为什么不能做这件事?”如果可以修改源码,你便可以自行添加一个新函数;如果不能,你就得在客户端编码,补足你要的那个函数。
如果客户类只使用这项功能一次,那么额外编码工作没什么大不了,甚至可能根本不需要原本提供服务的那个类。然而,如果你需要多次使用这个函数,就得不断重复这些代码。重复代码是软件万恶之源。这些重复代码应该被抽出来放进一个函数中。进行本项重构时,如果你以外加函数实现一项功能,那就是一个明确信号:这个函数原本应该在提供服务的类中实现。
如果你发现自己为一个服务类建立了大量外加函数,或者发现有许多类需要同样的外加函数,就不应该再使用本项重构,而应该使用 Introduce Local Extension (引入本地扩展)。
但是不要忘记:外加函数终归是权宜之计。如果有可能,你仍然应该将这些函数搬移到它们的理想家园。如果由于代码所有权的原因使你无法做这样的搬移,就把外加函数交给服务类的提供者,请他帮你在服务类中实现这个函数。
做法:1、在客户类中建立一个函数,用来提供你需要的功能。这个函数不应该调用客户类的任何特性。如果它需要一个值,把该值动作参数传给它。
2、以服务类实例作为该函数的第一个参数。
3、将该函数注释为:”外加函数“,应在服务类中实现。这么一来,如果将来有机会将外加函数搬移到服务类时,你便可以轻松找出这些外加函数。
重构手法17:Introduce Local Extension (引入本地扩展)
你需要为服务类提供一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。
动机:类的作者无法预知未来,他们常常没能为你预先准备一些有用的函数。如果你可能修改源码,最后的办法就是直接加入自己需要的函数。但你经常无法修改源码。如果只需要一两个函数,你可以使用 Introduce Foreign Method (引入外加函数)。但如果你需要的额外函数超过2个,外加函数就很难控制它们了。所以你需要将这些函数组织在一起,放到一个恰当的地方去。要达到这个目的,2种标准对象技术—子类化(subclassing)和包装(wrapping)是显而易见的办法。这种情况下,把子类化和包装类统称为本地扩展。
所谓本地扩展是一个独立的类,但也是被扩展类的字类型:它提供源类的一切特性,同时额外添加新特性。在任何使用源类的地方,你都可以使用本地扩展取而代之。
使用本地扩展使你得以坚持:函数和数据应该被统一封装“的原则。如果你一直把本该放在扩展类中的代码零散的放置于其他类中,最终只会让其他这些类变得过分复杂,并使得其他函数难以被复用。
在子类和包装类之间做选择时,首选子类。因为这样的工作量比较少。制作子类的最大障碍在于,它必须在对象创建期实施。如果可以接管对象创建过程,那当然没问题;但如果你想在对象创建之后再使用本地扩展,就有问题了。此时,子类化方案还必须产生一个子类对象,这种情况下,如果有其他对象引用了旧对象,我们就同时有2个对象保存了原数据。如果原数据是不可修改的,那也没问题。可以放心进行复制;但如果原数据允许修改,问题就来了,因为一个修改动作无法同时改变2份副本。这时候就必须改用包装类。使用包装类时,对本地扩展的修改会波及原对象,反之亦然。
做法:1、建立一个扩展类,将它作为原始类的子类或包装类。
2、在扩展类中加入转型构造函数。所谓“转型构造函数“是指”接受原对象作为参数“的构造函数。如果采用子类化方案,那么转型构造函数应该调用适当的超类构造函数。如果采用包装类,那么转型构造函数应该将它得到的传入参数以实例变量的形式保存起来,用做接受委托的原对象。
3、在扩展类中加入新特性。
4、根据需要,将原对象替换为扩展对象。
5、将针对原始定义的所有外加函数搬移到扩展类中。
重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)的更多相关文章
- 重构改善既有代码设计--重构手法05:Introduce Explaining Variable (引入解释性变量)
发现:你有一个复杂的表达式. 解决:将该复杂的表达式(或其中的部分)的结果放进一个临时变量,并以此变量名称来解释表达式用途. //重构前 if((platform.toUpperCase().in ...
- 重构改善既有代码设计--重构手法04:Replace Temp with Query (以查询取代临时变量)
所谓的以查询取代临时变量:就是当你的程序以一个临时变量保存某一个表达式的运算效果.将这个表达式提炼到一个独立函数中.将这个临时变量的所有引用点替换为对新函数的调用.此后,新函数就可以被其他函数调用. ...
- 重构改善既有代码设计--重构手法01:Extract Method (提炼函数)
背景: 你有一段代码可以被组织在一起并独立出来.将这段代码放进一个独立函数,并让函数名称解释该函数的用途. void PrintOwing(double amount) { PrintBanner() ...
- 重构改善既有代码设计--重构手法11:Move Field (搬移字段)
你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段. 动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...
- 重构改善既有代码设计--重构手法08:Replace Method with Method Object (以函数对象取代函数)
你有一个大型函数,其中对局部变量的使用,使你无法釆用 Extract Method. 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的值域(field) 然后你可以在同一个对象中将这个大型 ...
- 重构改善既有代码设计--重构手法07:Remove Assignments to Parameters (移除对参数的赋值)
代码对一个 参数赋值.以一个临时变量取代该参数的位置. int Discount(int inputVal, int quantity, int yearTodate) { if (input ...
- 重构改善既有代码设计--重构手法02:Inline Method (内联函数)& 03: Inline Temp(内联临时变量)
Inline Method (内联函数) 一个函数调用的本体与名称同样清楚易懂.在函数调用点插入函数体,然后移除该函数. int GetRating() { return MoreThanfiveLa ...
- 重构改善既有代码设计--重构手法19:Replace Data Value with Object (以对象取代数据值)
你有一笔数据项(data item),需要额外的数据和行为. 将这笔数据项变成一个对象. class Order... private string customer; ==> class Or ...
- 重构改善既有代码设计--重构手法13:Inline Class (将类内联化)
某个类没有做太多事情.将这个类的所有特性搬移到另一个类中,然后移除原类. 动机:Inline Class (将类内联化)正好于Extract Class (提炼类)相反.如果一个类不再承担足够责任.不 ...
随机推荐
- 【状压dp】AC Challenge
https://nanti.jisuanke.com/t/30994 把每道题的前置条件用二进制压缩,然后dp枚举所有可能状态,再枚举该状态是从哪一个节点转移来的,符合前置条件则更新. 代码: #in ...
- ERROR----java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils
2013-4-28 13:17:57 org.apache.catalina.core.StandardContext filterStart 严重: Exception starting filte ...
- 转 docker的下载与安装
百度网盘下载地址:https://oomake.com/download/docker-windows 道客地址: https://link.jianshu.com/?t=https://get.da ...
- 各大巨头电商提供的IP库API接口-新浪、搜狐、阿里
新浪的IP地址查询接口:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js (不可用)新浪多地域测试方法:http://i ...
- 微信小程序 功能函数 地图定位相对直线距离
GetDistance:function(lat1, lng1, lat2, lng2){ // console.log(lat1) var radLat1 = lat1 * Math.PI / ...
- [讲解]sql except和intersect运算符(比拟两个或多个select语句的结果并前去非重复值)
图 1 UNION 中若有重复的行,会被移除,只留下一个 1.简介 EXCEPT和INTERSECT运算符使您可以比较两个或多个SELECT语句的结果并返回非重复值. 2.区别 EXCEPT运算符返回 ...
- 【数据库】百万级数据库SQL优化大总结
网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后,感觉 ...
- Spring Cloud Zuul(服务网关)
新建应用:api-gateway pom.xml <?xml version="1.0" encoding="UTF-8"?> <projec ...
- 贪心(qwq)习题题解
贪心(qwq)习题题解 SCOI 题解 [ SCOI2016 美味 ] 假设已经确定了前i位,那么答案ans一定属于一个区间. 从高位往低位贪心,每次区间查找是否存在使此位答案为1的值. 比如6位数确 ...
- [ZJOI2011]细胞——斐波那契数列+矩阵加速+dp
Description bzoj2323 Solution 题目看起来非常复杂. 本质不同的细胞这个条件显然太啰嗦, 是否有些可以挖掘的性质? 1.发现,只要第一次分裂不同,那么互相之间一定是不同的( ...