电子商务网站建设实训室简介,石家庄网站建设方案,电子商务营销写作实务,金融集团网站建设方案1.1 何为重构#xff0c;为何重构
第一个定义是名词形式#xff1a;
重构#xff08;名词#xff09;#xff1a;对软件内部结构的一种调整#xff0c;目的是在不改变「软件可察行为」前提下#xff0c;提高其可理解性#xff0c;降低修改成本。
「重构」的另一个用…1.1 何为重构为何重构
第一个定义是名词形式
重构名词对软件内部结构的一种调整目的是在不改变「软件可察行为」前提下提高其可理解性降低修改成本。
「重构」的另一个用法是动词形式
重构动词使用一系列重构准则手法在不改变「软件可察行为」前提 下调整其结构。 改进软件设计使软件更易被理解。 ps重构是一种经济适用行为而非道德使然如果它不能让我们更快更好的开发那么它是毫无意义。 重构对个体程序员的意义是提高ROI。 更快速的定位问题节省调试时间。最小化变更风险提高代码质量减少修复事故的时间。得到程序员同行的认可更好的发展机会。 重构对整个研发团队的意义是战斗力的提升。 1.2 什么时候需要重构?
三次法则
添加功能更时重构
修补错误时重构
复审代码时重构
Code review : 在给别人code review时嗅出坏味道在不失礼貌的前提下提出建议。每次 commit 代码时: 每一次经你之手提交的代码都应该比之前更加干净。当你接手一个异常难读的项目时: 说服项目组将重构作为一项需求任务来做。当迭代效率低于预期时: 将重构当作一个项任务专门来做必要的时候停下来迭代需求。 重构过程中关于两顶帽子的比喻 使用重构技术开发软件时你把自己的时间分配给两种截然不同的行为「添加新功能」和「重构」。 添加新功能时你不应该修改既有代码只管添加新功能。重构时你就不能再添加功能只管改进程序结构。 软件开发过程中你可能会发现自己经常变换帽子。首先你会尝试添加新功能然后觉得把程序结构改一下功能的添加会容易得多。于是你换一顶帽 子做一会儿重构工作。接着重复该过程。整个过程或许只花十分钟但无论何时你都应该清楚自己戴的是哪一顶帽子。 二 代码/架构的坏味道
何时重构书中告诉了你一些迹象它会指出「这里有一个可使用重构解决的问题」。
详细可参考导图篇幅所限下面举例了其中15条重点在介绍这种“坏味道”相应的应对方法可以参考https://www.itzhai.com/articles/bad-code-small.html
2.1 Mysterious Name神秘命名
好的名字能节省未来用在猜谜上的大把时间。 源代码
function getPrice(order) {const a order.quantity * order.itemPriceconst b Math.max(0, order.quantity - 500) * order.itemPrice * 0.05const c Math.min(basePrice * 0.1, 100)return a - b c
}改进
function getPrice(order) {// 获取基础价格const basePrice order.quantity * order.itemPrice// 获取折扣const quantityDiscount Math.max(0, order.quantity - 500) * order.itemPrice * 0.05// 获取运费const shipping Math.min(basePrice * 0.1, 100)// 计算价格return basePrice - quantityDiscount shipping
}2.2 Duplicated Code重复的代码
“如果你在一个以上的地点看到相同的代码结构那么可以肯定设法将它们合而为一程序会变得更好。一旦有重复代码存在阅读这些重复的代码时你就必须加倍仔细留意其间细微的差异。如果要修改重复代码你必须找出所有的副本来修改。”
2.3 Long Method过长函数
“据我们的经验活得最长、最好的程序其中的函数都比较短。初次接触到这种代码库的程序员常常会觉得“计算都没有发生”——程序里满是无穷无尽的委托调用。但和这样的程序共处几年之后你就会明白这些小函数的价值所在。间接性带来的好处——更好的阐释力、更易于分享、更多的选择——都是由小函数来支持的。”
2.4 Large Class过大类 2.5 Long Parameter List过长参数列
“刚开始学习编程的时候老师教我们把函数所需的所有东西都以参数的形式传递进去。这可以理解因为除此之外就只能选择全局数据而全局数据很快就会变成邪恶的东西。但过长的参数列表本身也经常令人迷惑。”
public class LongParameterListExample {public void processData(String name, String address, int age, String gender, String occupation, String phoneNumber, String email) {// process data here}
}上面的代码定义了一个方法processData它有7个参数。在这个例子中我们可以发现参数列非常长不利于程序的可读性和可维护性。这是一个典型的过长参数列的例子。
2.6 Divergent Change发散式变化
指一个类受多种变化的影响。
你发现你想要修改一个函数却必须要同时修改许多不相关的函数。例如当你想要添加一个新的产品类型时你需要同步修改对产品进行查找、显示、排序的函数。
2.7 Shotgun Surgery霰弹式修改
多种变化引发多个类相应的修改。
任何修改都需要在许多不同类上做小幅度修改。
可能原因一个单一的职责被拆分成大量的类。 注意霰弹式修改 与 发散式变化 区别 : 发散式变化是在一个类受多种变化影响, 每种变化修改的方法不同, 霰弹式修改是 一种变化引发修改多个类中的代码。
2.8 Feature Envy依恋情结 函数大量地使用了另外类的数据。这种情况下最好将此函数移动到那个类中。 函数对某个class的兴趣高过对自己所处之 class的兴趣。无数次经验里我们看到某个函数为了计算某值从另一个对象那儿调用几乎一半以上的取值函数。 影响数据和行为不在一处修改不可控。 解决方案让数据和行为在一起通过 Extract Method提炼函数和Move Method搬移函数的方法来处理这函数到该去的地方。
2.9 Data Clumps数据泥团
数据泥团指的是经常一起出现的数据比如每个方法的参数几乎相同处理方式与过长参数列的处理方式相同用Introduce Parameter Object引入参数对象将参数封装成对象。
2.10 Primitive Obsession基本型别偏执
写代码时总喜欢用基本类型来当参数而不喜欢用对象。当要修改需求和扩展功能时复杂度就增加了。
2.11 Lazy Element冗赘的元素
去除多层不必要的包装。
如方法a中包的是bb包的是cc包的是d。但是bc只是基于某种考虑的纯粹包装而从未有其他变化这时可以让a直接包dbc就去掉吧。
class Customer {private String name;private String address;private String city;private String state;private String zip;private String phone;private String email;public Customer(String name, String address, String city, String state, String zip, String phone, String email) {this.name name;this.address address;this.city city;this.state state;this.zip zip;this.phone phone;this.email email;}public String getEmail() {return email;}
}class Order {private Customer customer;private int total;public Order(Customer customer, int total) {this.customer customer;this.total total;}public String getCustomerEmail() {return customer.getEmail();}
}在上面的代码中我们定义了一个Order类和一个Customer类其中Order类知道Customer类的详细信息但仅使用Customer的电子邮件。这是一个冗赘的元素因为只需要知道用户的电子邮件但是却存储了大量未使用的数据。在这种情况下重构可能会改为
class CustomerEmail {private String email;public CustomerEmail(String email) {this.email email;}public String getEmail() {return email;}
}class Order {private CustomerEmail customerEmail;private int total;public Order(CustomerEmail customerEmail, int total) {this.customerEmail customerEmail;this.total total;}public String getCustomerEmail() {return customerEmail.getEmail();}
}现在我们只存储所需的信息而不是冗赘的信息这样可以使代码更简洁。
2.12 Message Chains过长的消息链
向一个对象请求另一个对象然后再向后者请求另一个对象然后再请求另一个对象……
未充分的考虑数据结构的读取场景导致在需要使用某些数据的时候无法简单的获得其引用或者为了使用某个字段需要了解一堆中间封装的数据结构。
a.b.c.d.e()2.13 Middle Man中间人
对象的基本特征之一就是封装——对外部世界隐藏其内部细节。封装往往伴随着委托。比如你问主管是否有时间参加一个会议他就把这个消息“委托”给他的记事簿然后才能回答你。很好你没必要知道这位主管到底使用传统记事簿还是使用电子记事簿抑或是秘书来记录自己的约会。
但是人们可能过度运用委托。你也许会看到某个类的接口有一半的函数都委托给其他类这样就是过度运用。这时应该使用移除中间人直接和真正负责的对象打交道。如果这样“不干实事”的函数只有少数几个可以运用内联函数把它们放进调用端。如果这些中间人还有其他行为可以运用以委托取代超类或者以委托取代子类把它变成真正的对象这样你既可以扩展原对象的行为又不必负担那么多的委托动作。
2.14 Refused Bequest被拒绝的遗赠
子类应该继承超类的函数和数据。但如果它们不想或不需要继承又该怎么办呢?它们得到所有礼物却只从中挑选几样来玩!
按传统说法这就意味着继承体系设计错误。你需要为这个子类新建一个兄弟类再运用函数下移和字段下移把所有用不到的函数下推给那个兄弟。这样一来超类就只持有所有子类共享的东西。你常常会听到这样的建 议:所有超类都应该是抽象(abstract)的。
2.15 注释Comments 废话注释与代码逻辑不一致的注释尽量让提炼的函数和精炼易懂的命名减少注释的必要
参考资料
https://refactoringguru.cn/
速看笔记版
https://www.itzhai.com/articles/refactoring-cheat-sheet.html
https://www.itzhai.com/articles/bad-code-small.html
《重构》笔记—坏代码的味道与处理
坏味道与重构手法速查表