《重构》读书笔记

《重构》读书笔记

马草原 1,314 2023-04-15

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

refactor

《重构:改善既有代码的设计》是一本经典软件工程书籍,由Martin Fowler等人合著。本书深入探讨了如何通过逐步、小步骤的方式对现有代码进行改进,以提高代码的可维护性和可读性,让软件系统更易于理解和维护。通过实际的重构技术、模式和示例,帮助开发者有效地优化代码,避免陷入混乱、复杂的代码困境,从而为软件开发提供实用指导。

一、什么是重构?

不改变外在行为,而提高代码质量
重构前,先检查自己是否有一套可靠的测试集。这些测试必须有自我检验能力。

二、为何要重构?

1. 重构改进软件的设计使其变得更容易维护

随着项目的发展和需求迭代,代码会变得越来越复杂,难以理解和修改。通过重构,我们可以将复杂的代码分解为更小、更简单的部分,使得维护和修改变得更加容易和安全。

2. 重构使软件更容易理解

重构可以帮助我们改善代码的可读性、可维护性和可扩展性。通过重构,我们可以消除重复代码、提取通用功能、简化复杂的逻辑等,从而使代码更加清晰、简洁和易于理解。

3. 重构帮助找到bug

如果对代码进行重构,我就可以深入理解代码的所作所为,并立即把新的理解反映在代码当中。搞清楚程序结构的同时,我也验证了自己所做的一些假设,于是想不把bug揪出来都难。

4. 重构提高编程速度

重构可以帮助我们更好地理解代码,发现潜在的问题和改进的机会。通过重构,我们可以逐步改进代码的设计和结构,从而提高开发效率和代码质量。

三、何时重构?

1. 添加新功能时

当需要添加新功能时,如果现有的代码结构不够清晰或存在重复代码,就可以考虑进行重构。通过重构,可以使代码更加灵活和可扩展,为新功能的添加提供更好的支持。

2. 修复Bug时

当需要修复一个Bug时,如果发现现有的代码结构不够清晰或存在逻辑错误,就可以考虑进行重构。通过重构,可以消除潜在的问题和错误,提高代码的可读性和可维护性。

3. 代码审查时

在进行代码审查时,如果发现代码存在重复、冗余或复杂的部分,就可以考虑进行重构。通过重构,可以提高代码的质量和可读性,减少潜在的问题和错误。

4. 代码演进时

随着项目的发展和演化,代码可能会变得越来越复杂和难以理解。在代码演进的过程中,如果发现代码结构不够清晰或存在重复代码,就可以考虑进行重构。通过重构,可以使代码更加简洁、清晰和易于理解。

5. 长期重构:

不应该把重构当做独立的需求,更不应该把重构交给特定的人去做。应该在团队中达成共识,让团队成员都具有重构意识,在日常需求迭代中逐步优化系统,并在系统的不断演进时不断思考不断重构。

需要去修改、理解的代码需要重构。
重构也是有成本的,如果一段代码是临时的、或者永远也不会对其修改的代码,是一种不值得重构的情况。

四、代码的坏味道

1. 令人费解的命名

2. 重复代码

3. 过长函数

4. 过长参数列表

5. 全局数据

全局数据指的是在整个程序中都可以访问和修改的数据。比如全局变量或者静态变量,可以被任何代码修改和使用。全局数据的问题在于它们破坏了封装性和模块化,在系统日渐复杂的演化下我们很难知道这个全局变量都有谁用的最终使得代码的依赖关系变得复杂,难以理解和维护。全局数据的修改可能会对整个程序产生意想不到的影响,增加了代码的耦合性和不确定性。

6. 可变数据

可变数据指的是可以被修改的数据。在函数或方法中,如果使用了可变数据,那么这个函数或方法的行为可能会受到外部数据的影响,导致代码的可预测性降低。可变数据的问题在于它们增加了代码的复杂性和难以理解性,使得代码的调试和测试变得困难。此外,可变数据还可能导致并发问题和竞态条件,使得程序的正确性受到威胁。
为了解决全局数据和可变数据的问题,可以采取以下几种方法:

  1. 将全局数据转化为局部数据:尽量避免使用全局变量,而是将数据限制在函数或模块的作用域内。可以通过全局变量作为参数传递给函数,者将全局变量封装在一个类中来实现。
  2. 尽量使用不可变数据:尽量不要在函数中使用外部数据,要用应该用不可变的数据,比如final修饰的常量。
  3. 使用封装和访问控制:通过封装数据和使用访问控制来限制对数据的访问和修改,可以使用get/set方法访问。
  4. 发散式变化
    发散式变化指的是当需要修改一个类时,需要在多个不相关的地方进行修改。这意味着这个类承担了过多的责任,违反了单一职责原则。发散式变化使得代码的修改变得困难和复杂,增加了代码的维护成本。当一个类承担了过多的功能和责任时,它的修改会影响到多个不相关的功能,导致代码的耦合性增加。为了解决发散式变化,可以通过将类的功能进行拆分,将不相关的功能分离到不同的类中,使得每个类只负责一个单一的职责。

8. 霰弹式修改

霰弹式修改指的是当需要修改一个功能时需要在多个类中进行修改。这意味着该功能的实现分散在多个类中,违反了高内聚原则。霰弹式修改使得代码的修改变得困难和容易出错,增加了代码的耦合性。当一个功能的实现分散在多个类中时,每次修改该功能都需要在多个类中进行修改,导致代码的维护成本增加。为了解决霰弹式修改,可以通过将相关的功能集中到一个类中,使得修改只需要在一个地方进行。

9. 依恋情结

依恋情结指的是一个类对另一个类的某个特定特性(方法、属性等)表现出过度的依赖。这种情况下,一个类对另一个类的某个特性的使用比自己的特性使用更频繁,甚至可能超过了自己的职责范围。这种依恋情结导致了代码的耦合性增加,使得代码难以理解、维护和扩展。

8. 数据泥团

数据泥团指的是在代码中存在多个地方使用相同的数据组合,这些数据组合应该被封装成一个单独的数据结构(比如封装成一个对象)。

9. 基本类型偏执

基本类型偏执是指对于某个业务,过分地使用基本数据类型(如int、String、boolean等),而不是创建一个单独的对象来表示。这种偏执会导致代码中充斥着大量的基本数据类型,无法看出具体的业务含义,使代码难以理解、扩展和维护。

10. 重复的Switch

重复的switch是指在代码中多次现相同的switch语句,这些switch语句执行相似的逻辑,但却在不同的地方重复出现。这种重复的switch语句会导致代码冗余,可读性差,且难以维护和扩展。

11. 循环语句

循环语句是指在代码中使用for、while、do-while等循环语句来重复执行的代码。循环语句是程序设计的核心要素,但过多的循环语句会导致代码难以理解、扩展和维护。

12. 冗赘的元素

冗赘的元素是指在代码中存在一些不必要的程序元素(如类和函数),它们并没有为代码增加结构或提供额外的功能,反而使代码变得冗长和复杂。

13. 夸夸其谈通用性

夸夸其谈通用性是指在代码中出现了过度设计或过度泛化的情况,产生了一些不必要的或者未被实际使用的方法。

14. 临时字段

临时字段是指在一个类中存在的一个字段,它只在某些特定的情况下被使用,而在其他情况下则没有任何作用。

15. 过长的消息链

过长的消息链是指在代码中存在一长串的方法调用,用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象。

16. 中间人

中间人是指在代码中存在一些没有实质性功能的类或方法,它们只是作为其他类或方法之间的中转或包装。

17. 内幕交易

开发者应该尽量减少模块之间的数据交换,将数据交换放在明面上,以减少模块之间的耦合。

18. 过大过长的类(违背了单一职能原则)

19. 异曲同工的类

异曲同工的类是指在代码中存在两个或多个类,它们具有相似的功能,但接口不一致,因此这些类之间不能进行替换,可以通过重构让它们的接口一致。

20. 纯数据类

纯数据类是指那些只包含数据字段和访问这些字段的方法,没有实际的业务逻辑或行为的类。这些类通常被其他类过度地操作和操控,它们只是一种无法主动做出行为的数据容器。
如果纯数据类拥有公共字段,可以使用封装记录重构方法将其封装起来,提供更好的封装和访问控制。
对于不应该被其他类修改的字段,可以使用移除设值函数重构方法,将其设值函数移除,使其成为只读字段。
找出纯数据类的访问方法被其他类调用的地方,尝试将这些调用行为搬移到纯数据类中,或者提炼出可被搬移的函数。

21. 被拒绝的遗赠

被拒绝的遗赠是指在面向对象编程中,当一个子类从父类继承了一些方法或行为,然后在子类中拒绝或不愿意使用继承的这些方法或行为。这种情况一般表明我们的抽象父类设计有问题。

22. 良好的注释(在代码整洁之道的笔记中已经详细总结)

五、自测试代码的价值

1. 提供安全网: 自测试代码可以作为一个安全网,帮助我们更早地发现引入的bug。每当进行重构时,测试用例可以确保代码在重构后仍然逻辑正确。

2. 缩减调试时间: 自测试代码能够迅速报告问题,帮助我们定位错误的位置。这样可以大大缩减调试的时间,让我们更专注于解决bug而不是花费大量时间去发现bug。

3. 提高代码质量: 编写自测试代码迫使我们思考代码的正确性和健壮性。良好的测试用例可以帮助我们发现边界情况和潜在的错误,从而提高代码质量。

4. 支持持续集成和自动化测试: 自测试代码是实现持续集成和自动化测试的基础。它们可以在代码发生变更时自动运行,保证新的修改不会破坏现有的功能。

六、重构名录

1. 名称(name):要建造一个重构词汇表,名称是很重要的。这个名称也就是我将在本书其他地方使用的名称。如今重构经常会有多个名字,所以我会同时列出常见的别名。

2. 速写(sketch) :这部分可以帮助你更快找到你所需要的重构手法。

3. 动机(motivation) :为你介绍“为什么需要做这个重构”和“什么情况下不该做这个重构”。

4. 做法(mechanics):简明扼要地一步一步介绍如何进行此重构。

5. 范例(examples) :以一个十分简单的例子说明此重构手法如何运作。

七、重构方法

提炼函数:将函数中的一部分代码提炼出来,形成一个独立的新函数。这样可以提高代码的可读性、可重用性和维护性。适用于过长的函数、代码块或重复代码。

内联函数:将函数调用处替换为函数本体,用于简化代码结构和减少不必要的函数调用。但要注意内联函数可能引起过多的代码重复,应谨慎使用。

内联临时变量:将临时变量替换为其表达式本体,以减少不必要的临时变量,提高代码清晰度。

以查询取代临时变量:将计算结果赋值给临时变量的代码,改为直接使用查询表达式,从而消除临时变量,使代码更加简洁。

引入解释性变量:通过引入一个有意义的变量来解释复杂的表达式或计算,提高代码的可读性。

分解临时变量:如果一个临时变量被赋值多次,可以将其拆分为多个临时变量,每个变量只负责一次赋值,使代码更易于理解。

移除对参数的赋值:避免在函数内部对传入的参数进行赋值,以增强代码的可读性和可维护性。

以函数对象取代函数:当一个函数过于复杂,难以拆分时,可以将其重构为一个单独的类,将原来的函数作为该类的一个方法,以便更好地管理复杂逻辑。

八、封装

为什么要封装?

封装是一种重要的面向对象编程原则,它将数据和行为包装在一个单元内部,隐藏内部的实现细节,只暴露必要的接口供外部使用。封装的目的是降低模块之间的耦合度,提高代码的可维护性、安全性和可复用性。

1. 封装记录

  • 目的:将直接访问的记录数据封装成一个独立的类,隐藏记录的内部表示,提供更清晰的接口。
  • 解决方案:创建一个新的类,将记录的字段作为私有属性,通过公共访问器方法暴露记录数据。
  • 优势:减少对记录数据的直接访问,提高代码的可维护性和可扩展性。

2. 封装集合

  • 目的:将对集合的直接访问封装成独立的集合类,限制集合的修改和访问权限。
  • 解决方案:创建一个新的集合类,在其中添加对集合的操作方法,并将原有集合的访问方法改为调用新集合类的方法。
  • 优势:避免集合在代码中被不当修改,增加了对集合的控制和管理能力。

3. 以对象取代基本类型

  • 目的:将基本类型数据封装为对象,提供更丰富的行为和功能。
  • 解决方案:创建一个新的类,将原有基本类型数据作为其属性,并在该类中添加相关操作方法。
  • 优势:增强代码的可读性和可维护性,更好地封装数据,便于添加新的行为。

4. 以查询取代临时变量

  • 目的:将临时变量的计算逻辑封装为查询方法,避免使用临时变量。
  • 解决方案:将临时变量的计算逻辑提炼为一个独立的查询方法,并在需要时调用该方法。
  • 优势:减少临时变量的使用,提高代码的清晰度和可维护性。

5. 提炼类

  • 目的:将一个类中相关的一组数据和方法提取到一个新的类中,以提高类的内聚性和清晰度。
  • 解决方案:创建一个新的类,将相关数据和方法从原类中搬移到新类中,并在原类中创建对新类的引用。
  • 优势:使得类的设计更加聚焦,避免类的功能过于庞杂。

6. 内联类

  • 目的:当一个类变得太过简单或不再有实际作用时,将其内联到其他类中。
  • 解决方案:将原类的字段和方法直接搬移到其他类中,并移除原类。
  • 优势:简化类的层次结构,去除不必要的中间类。

7. 隐藏委托关系

  • 目的:减少客户端代码与委托类之间的直接耦合,提高代码的灵活性。
  • 解决方案:在客户端类中添加委托方法的包装方法,隐藏对委托类的直接访问。
  • 优势:减少代码的依赖关系,客户端类更加解耦,易于维护和修改。

8. 移除中间人

  • 目的:当一个类只是作为委托的中间人,没有其他实质性的功能时,可以移除该中间人。
  • 解决方案:直接调用委托类的方法,去除中间人类。
  • 优势:简化代码逻辑,减少不必要的委托。

9. 替换算法

  • 目的:用一个更清晰或更高效的算法替换原有的复杂算法。
  • 解决方案:先编写一个新的算法,确保其结果与原算法一致,然后逐步替换原有算法。
  • 优势:提高代码的可读性和性能。

九、搬移特性

搬移字段:

  • 原因:某个字段在一个类中与其它字段和方法关联较少,更适合与另一个类相关联,以增强代码的内聚性。
  • 目的:将字段放置在更合适的类中,使得类的设计更加清晰和合理,提高代码的可读性和维护性。
  • 优势:减少类的职责,使类更加专注于特定的功能,避免类变得过于臃肿。同时,相关字段聚集在一起,便于理解和修改。

搬移方法:

  • 原因:某个方法更适合属于另一个类,因为它与目标类的数据更紧密相关。
  • 目的:将方法移到目标类中,增强代码的内聚性,让方法与数据更加紧密地结合在一起。
  • 优势:改进代码组织,提高可读性和可维护性。同时,让代码更符合面向对象设计的原则,提高代码的灵活性和扩展性。

以字段取代子类:

  • 原因:多个子类之间的唯一区别仅仅在于一个字段的值,导致类继承关系过于复杂。
  • 目的:将不同子类合并为一个类,并用字段表示它们之间的差异,简化继承结构。
  • 优势:减少类的数量,简化代码结构,降低维护成本。同时,更符合面向对象设计的"组合优于继承"原则。

以委托取代子类:

  • 原因:子类仅仅是为了调用父类的方法,而没有添加额外的行为。
  • 目的:使用委托来替代继承,使代码更加灵活,减少继承层次。
  • 优势:简化继承结构,使代码更加清晰和易于维护。同时,减少了子类的数量,降低了类之间的耦合。
public class Order {
    private String orderId;
    private Money amount;
    // 其他属性和方法
    
    public void checkAmount() {
        // 处理校验订单金额的逻辑
    }
    
    // 其他订单处理逻辑
}

public class PaymentOrder extends Order {
    // 使用继承checkAmount()方法
}

public class RefundOrder extends Order {
    // 使用继承checkAmount()方法
}
public class Order {
    private String orderId;
    private Money amount;

    
    private PaymentService paymentService = new PaymentService();

    // 委托给支付服务类的checkAmount()方法
    // 这样可以减少子类的数量,抽离一些共有逻辑,使代码更加灵活和易于维护。有其他订单的checkAmount逻辑发生变化时,只需修改支付服务类,而不需要修改Order类。
    public void checkAmount() {
        paymentService.processPayment(this); 
    }
    
}

public class PaymentService {
    public void checkAmount(Order order) {
        // 聚合处理逻辑
    }
}

提炼类:

  • 原因:一个类负责了过多的职责,导致类变得庞大复杂。
  • 目的:将部分职责提取出来形成一个新的类,使每个类负责单一职责。
  • 优势:改善类的设计,提高代码的可读性和可维护性。同时,使类的设计更加清晰,更符合面向对象设计原则。

内联类:

  • 原因:某个类变得太小或者没有足够的独立原因来存在。
  • 目的:将类的内容内联到另一个类中,消除不必要的类结构。
  • 优势:简化代码结构,减少类的数量,使代码更加紧凑和易于理解。

隐藏委托关系:

  • 原因:客户端类频繁访问一个对象的委托对象,导致客户端代码过于冗长。
  • 目的:将委托关系隐藏起来,让客户端类直接与委托对象交互,简化客户端代码。
  • 优势:提高代码的可读性和可维护性,减少客户端代码与委托对象的耦合。
public class LogPrinter {
    public void printLog(String log) {
        // 打印文档的逻辑
        System.out.println("Printing log: " + document);
    }
}
public class LogProcessor {
    private LogPrinter printer;

    public LogProcessor() {
        this.printer = new LogPrinter();
    }

    public void processLog(String log) {
        // 调用委托对象的方法
        // 暴露了委托关系导致和委托对象直接耦合了
        printer.printLog(log);
    }
}
public class LogProcessor {
    private LogPrinter printer;

    public LogProcessor() {
        this.printer = new LogPrinter();
    }

    public void processDocument(String document) {
        // 调用封装委托后的方法 隐藏了委托关系
        printLog(document); 
    }

    // 隐藏委托关系
    private void printLog(String document) {
        // 调用委托给委托对象的方法
        // 解除和委托对象的直接耦合
        printer.printLog(document); 
    }
}

移除中间人:

  • 原因:某个类仅仅委托了其他类的方法,没有添加任何额外的功能。
  • 目的:移除这个中间人类,直接让客户端类与被委托的类进行交互。
  • 优势:简化代码结构,减少类之间的耦合,使代码更加直接和易于维护。

十、重新组织数据

拆分变量:

  • 原因:一个变量承载多个含义导致变量庞大或含义不清晰。
  • 目的:将一个复杂的变量拆分成多个明确表达含义的变量。
  • 优势:提高代码的可读性和可维护性,减少命名歧义,避免变量含义混淆。

字段改名:

  • 原因:字段名称不准确或与其他命名不一致。
  • 目的:改变字段的名称,使其更准确地反映其用途或与其他命名风格保持一致。
  • 优势:增强代码的可读性,减少误解,提高代码的一致性和理解性。

以查询取代派生变量:

  • 原因:存在派生变量,但重复计算可能导致性能问题或代码重复。
  • 目的:使用查询函数替代派生变量,消除重复计算。
  • 优势:提高代码的可维护性和性能,确保计算的准确性。
public class Product {
    private Double price;
    private Integer quantity;
    ...

    public Product(Double price, Integer quantity) {
        this.price = price;
        this.quantity = quantity;
    }

    public Double getPrice() {
        return price;
    }

    public Integer getQuantity() {
        return quantity;
    }

    // 增加这个查询方法
    public double getTotalPrice() {
        return price * quantity;
    }
}

Product mainProduct = Order.getMainProduct();
Product subProduct = Order.getSubProduct();

// 以getPrice查询方法取代派生变量
double mainTotal = mainProduct.getPrice() * mainProduct.getQuantity();
double subTotal = subProduct.getPrice() * subProduct.getQuantity();

double total = mainTotal + subTotal;

System.out.println("总价: " + total);
Product mainProduct = Order.getMainProduct();
Product subProduct = Order.getSubProduct();

// 派生变量
double total = mainProduct.getTotalPrice() + subProduct.getTotalPrice();

System.out.println("Total price: " + total);

将引用对象改为值对象:

  • 原因:对象之间的关联不再有意义,导致冗余和复杂性增加。
  • 目的:将引用对象改为它所引用的对象的一个拷贝,消除关联。
  • 优势:简化代码结构,降低对象之间的耦合,避免数据冗余。

将值对象改为引用对象:

  • 原因:多个对象频繁共享同一个值对象,造成数据冗余。
  • 目的:将值对象改为引用对象,避免数据冗余。
  • 优势:减少内存占用,提高数据一致性,增强代码的灵活性。

十一、简化条件逻辑

  • 原因:条件逻辑太复杂,代码易读性差,难以维护。
    分解条件表达式:
  • 目的:将复杂的条件表达式拆分成多个简单的条件,使代码更加清晰易读。
  • 优势:增强代码的可读性,减少重复代码,方便维护和修改条件逻辑。

合并条件表达式

  • 目的:将多个条件表达式合并成一个,消除重复判断,简化代码逻辑。
  • 优势:减少冗余代码,提高代码的可维护性和可理解性。

以卫语句取代嵌套条件表达式

  • 目的:通过使用卫语句提前处理特殊情况,减少嵌套,使代码更加扁平和易读。
  • 优势:减少嵌套层次,简化代码结构,提高代码可读性和可维护性。

以多态取代条件表达式

  • 目的:使用多态性替代条件表达式,根据对象的类型进行分支处理,增加代码的灵活性和可扩展性。
  • 优势:提高代码的可扩展性和可维护性,减少条件逻辑分支,使代码更易于扩展和修改。

引入特例

  • 目的:针对特殊情况引入特例处理,避免在整个代码中散布大量的条件判断,使代码逻辑更加简洁。
  • 优势:简化代码逻辑,使代码更具可读性和易维护性,将特殊情况从常规逻辑中分离出来。

引入断言

  • 目的:在代码中插入断言来检查代码执行的正确性,确保代码的假设和条件满足预期。
  • 优势:帮助开发人员快速定位和发现潜在的错误或bug,提高代码的健壮性和可靠性。

十二、重构API

将查询函数和修改函数分离:

通过将数据查询和修改操作分离,可以增加代码的清晰度和可维护性。降低了副作用的可能性,减少了意外的数据修改,使代码更易于理解和调试。

函数参数化:

将函数的一些硬编码值转化为函数的参数,提高了代码的通用性和灵活性。使函数更加可复用,能够适用于不同的上下文和需求,减少了代码的冗余。

移除标记参数:

标记参数(通过布尔值或枚举值传递的参数)会增加函数的复杂性和可读性。减少了函数的复杂性,使函数的用途更加明确,降低了出错的可能性。

保持对象完整:

确保在对象上进行操作时,不破坏对象的内部状态,以避免产生不一致性。保持对象的完整性有助于减少错误和数据损坏,提高代码的可维护性。

以查询取代参数:

通过在函数内部进行数据查询,避免了将大量参数传递给函数,简化了函数的调用。减少了函数参数的数量,使函数调用更加简洁,提高了代码的可读性。

以参数取代查询:

将函数内部的数据查询转化为函数参数,降低了函数之间的耦合度。增加了函数的独立性,使其更易于测试和重用,提高了代码的灵活性。

移除设值函数:

设值函数(用于修改对象状态的函数)可能导致对象状态的不可预测性。消除了直接修改对象状态的方式,使对象的状态变化更加可控,减少了副作用。

以工厂函数取代构造函数:

直接使用构造函数创建对象可能会难以管理复杂的构建逻辑。工厂函数提供了更灵活的方式来创建对象,允许在构建过程中进行更多的逻辑处理。

以命令取代函数:

某些情况下,将操作封装成命令对象比直接调用函数更加灵活。命令对象可以更好地管理操作的撤销、重做等需求,提供了更强大的操作控制。

以函数取代命令:

有些情况下,将命令对象的操作转化为函数调用可以简化代码。使代码更加直观,减少了命令模式引入的额外复杂性,提高了代码的可读性。

十三、处理继承关系

函数上移:

当子类中的某个方法与父类中的方法功能相似时,将该方法移到父类中,以消除重复代码。

字段上移:

如果多个子类中拥有相同的字段,将字段移到父类中,以避免重复定义和维护。

构造函数本体上移:

当多个子类的构造函数中存在相似的初始化逻辑时,将这些逻辑移到父类的构造函数中,以减少重复代码。

函数下移:

如果父类中的某个方法只被部分子类使用,将该方法移到需要的子类中,以保持更高的内聚性。

字段下移:

当字段只在部分子类中使用时,将其移到需要的子类中,以减少父类的复杂性。

以子类取代类型码:

当存在一个类型码决定对象行为时,通过创建不同子类来取代类型码,使得每个子类都具有特定行为。

移除子类:

如果子类的功能变得与父类相似,或者只剩下一个子类,可以考虑移除子类,直接使用父类。

提炼超类:

当多个子类拥有相似的属性和方法时,将这些相似之处提取到一个共同的父类中,以避免重复代码。

折叠继承体系:

当继承关系变得复杂且不必要时,可以将子类与父类合并,简化继承体系。

以委托取代子类:

当子类仅仅是为了继承而存在,可以用委托关系替代子类,将共用的方法移至另一个类中。

以委托取代超类:

当一个类不再适合作为父类,可以使用委托关系替代继承,将原本的子类作为另一个类的委托对象。



心得

通过阅读《重构:改善既有代码的设计》,我深刻体会到了重构在软件开发中的重要性与实际应用。这本书不仅向我介绍了重构的核心概念和技术,更重要的是,它启示我如何以一种系统性、有策略性的方式重构现有代码,从而使整个开发过程更具可维护性可扩展性可读性

在软件开发中,代码质量和可维护性是至关重要的,因为我们的系统会随着业务的发展而不断迭代不断修改,如果没有重构思想,代码将在多次迭代后腐化。通过重构,我学会了在保持功能不变的前提下,通过微小的调整和优化,消除冗余、简化复杂度,提高代码的整体质量。这不仅使得代码更易于理解,也有助于团队协作和持续演进。我意识到,持续重构不仅仅是一种技术手段,更是一种追求代码卓越的态度,它能够帮助我在软件开发中不断进步和成长。

通过阅读这本书,我还学会了更加审慎地进行代码修改。每一步重构都需要经过周详的计划和测试,以确保不会破坏现有的功能。这强调了测试驱动开发和持续集成的重要性,以保证代码质量和稳定性。

通过深入了解和在工作项目中实践重构技术,我不仅获得了优化代码的技能,更获得了一种不断追求卓越、不断改进的思维方式。这将在我未来的软件开发之旅中继续指引我,帮助我构建更健壮、更可维护的软件系统,为技术的进步和项目的成功贡献更多!