重构-改善既有代码的设计之代码的坏味道举例说明

重构-改善既有代码的设计之代码的坏味道举例说明

标签:重构-改善既有代码的设计

22种坏味道

Duplicated Code(重复代码)

Long Method(过长的方法)

Large Class(过大的类)

Long Parameter List(过长的参数列表)

Divergent Change(发散式变化)

对一个类的修改涉及到多个不相关的功能或行为时(类里有不相关的功能,违背了单一职责原则)

public class User {
    private String username;
    private String email;
    private String password;
    private Date registrationDate;
    
    // getters and setters
    
    public void sendWelcomeEmail() {
        // 发送欢迎邮件的逻辑
    }
    public void grantPermission(String permission) {
        // 授予用户特定权限的逻辑
    }

}

解决办法提取类(Extract Class)

将不相关的功能提取到独立的类中,让每个类专注于单一的职责。在这个例子中,可以将发送邮件的逻辑提取到一个EmailSender类中,将权限管理的逻辑提取到一个UserPermissionManager类中,从而减少User类的职责,使其更符合单一职责原则。

Shotgun Surgery(散弹式修改)

描述:当需要修改一个功能时,需要在代码的多个不相关部分进行多次小的修改,而不是集中在一个地方进行修改。

假设有一个名为Order的类,用于表示系统中的订单信息。该类包含了订单的基本属性和一些与订单相关的方法。

随着系统的演化,需求变更需要在订单发生变化时记录日志。于是开发人员决定在Order类的各个方法中添加记录日志的逻辑。

public class Order {
    private String orderId;
    private Date orderDate;
    private double totalAmount;
    
    // ...

    public void calculateTotalAmount() {
        // 计算订单总金额的逻辑
    }

    public void generateInvoice() {
        // 生成订单发票的逻辑
    }

    public void notifyCustomer() {
        // 通知顾客订单状态的逻辑
    }

    // ...
}

public class Order {
    // ...

    public void calculateTotalAmount() {
        // 计算订单总金额的逻辑

        // 记录日志的逻辑
    }

    public void generateInvoice() {
        // 生成订单发票的逻辑

        // 记录日志的逻辑
    }

    public void notifyCustomer() {
        // 通知顾客订单状态的逻辑

        // 记录日志的逻辑
    }

    // ...
}

成为坏味道的原因:修改时容易遗漏,同时,这种修改方式也增加了代码的重复性和维护的复杂性。

解决方案:提取方法,在需要的地方调用

Feature Envy(依恋情结)

当一个方法过度依赖于另一个类的特定特性或数据,而忽略了自身所在类的特性和数据时,就存在依恋情结。

假设有一个名为Customer的类,表示系统中的顾客信息。另外还有一个名为Order的类,表示顾客的订单信息。Order类中的某个方法过度依赖于Customer类的特定特性或数据。

职责划分不清,一个应该放在Customer类中的方法放在了Order类中(放在Customer中,其他类用的时候可以调用A类,放在Order类中,其他类用的时候需要依赖Customer,重写一遍计算逻辑或者调用Order,Order类在依赖与Customer类)

public class Order {
    private Customer customer;
    private List<String> shopList;
    // ...

    // 就算折后总金额
    public double calculateDiscountedTotal() {
        // 假设calculateDiscountedTotal方法依赖于Customer类的discount属性
        double discount = customer.getDiscount();
        // 根据折扣计算订单的折扣后总金额的逻辑
        // ...
        return discount * 商品总金额
    }

    // ...
}

在这个例子中,Order类的calculateDiscountedTotal方法过度依赖于Customer类的discount属性。这种情况违反了封装的原则,因为Order类过于了解Customer类的内部细节,导致了依恋情结。

依恋情结的一种方法是将方法移动到更适合的类中,以减少对其他类的依赖。在这个例子中,可以考虑将calculateDiscountedTotal方法移动到Customer类中,使得折扣计算的逻辑与Customer类关联。

public class Customer {
    // ...

    public double calculateDiscountedTotal(Order order) {
        double discount = getDiscount();
        // 根据折扣计算订单的折扣后总金额的逻辑
        // ...
    }

    // ...
}

这样一来,calculateDiscountedTotal方法与Customer类关联,避免了Order类对Customer类的依恋情结。Order类不再直接依赖于Customer类的具体属性,而是通过传递Order对象给Customer类的方法来实现折扣计算。

Feature Envy(依恋情结)的问题在于方法过度依赖于另一个类的特定特性或数据,而忽略了自身所在类的特性和数据。这导致了以下问题:

  1. 违反封装原则:依恋情结违反了类的封装性,因为方法过于关注其他类的内部细节。这使得类的设计不够独立和可维护,因为它过于依赖其他类的实现细节。
  2. 高耦合性:依恋情结导致类之间的耦合度增加。当一个类过度依赖另一个类时,它们之间的关系变得紧密,修改一个类可能会影响到其他类的功能。这增加了代码的脆弱性和复杂性。
  3. 低内聚性:依恋情结导致类的内聚性降低。一个类应该关注自身的职责和功能,而不是过度关注其他类的属性和数据。当方法过于关注其他类时,类的职责不清晰,难以理解和维护。
  4. 代码重复和冗余:依恋情结可能导致代码的重复和冗余。当多个类需要使用相同的功能或数据时,如果每个类都实现一份相似的代码逻辑,就会导致代码重复。这增加了维护成本,并可能导致一致性问题。
  5. 难以扩展和修改:依恋情结使得修改和扩展变得困难。当需要修改或扩展功能时,由于方法过于依赖其他类的特性和数据,修改点会分散在多个类中,增加了修改的复杂性和风险

解决办法:将方法移到更合适的类中

Data Clumps(数据泥团)

当多个数据项经常一起出现,并且它们总是以相同的方式组合在一起使用时,就存在数据泥团.

违反了数据库的第三范式:非主属性对主属性存在传递函数依赖关系(非主属性与非主属性之间存在函数依赖关系,例如f(用户名)->用户地址)

假设有一个名为Order的类,表示系统中的订单信息。该类包含了订单的基本属性,如订单号、客户信息、商品列表等。

public class Order {
    private String orderId;
    private String customerName;
    private String customerAddress;
    private List<String> itemList;
    
    // ...
}

在这个例子中,customerNamecustomerAddress两个属性经常一起使用,而且它们总是以相同的方式组合在一起。这种情况下,就存在数据泥团,因为这两个数据项可以组合成一个独立的类来表示客户信息,而不是分散在订单类中。

public class Order {
    private String orderId;
    private Customer customer;
    private List<String> itemList;
    
    // ...
}

public class Customer {
    private String name;
    private String address;
    
    // ...
}

为了解决数据泥团问题,可以使用重构手法如提炼类(Extract Class)将相关的数据项提取到一个新的类中,形成一个独立的客户类。

Primitive Obsession(基本类型偏执)

过度使用基本类型(如整数、字符串)来表示领域内的概念,而不是使用更具表达力和语义的自定义类型。

public class Product {
    private String name;
    private double price;
    private int quantity;
    
    // ...
}

如果有必要提取类的话,也就是刚开始基本类型可以实现,后来基本类型满足不了了,就没必要偏执了

public class Product {
    private String name;
    private Money price;
    private Quantity quantity;
    
    // ...
}

public class Money {
    private double value;
    
    // ...
}

public class Quantity {
    private int value;
    
    // ...
}

解决方案:提取类

Switch Statements (switch 惊悚现身)

假设有一个名为PaymentProcessor的类,负责处理不同类型的支付方式。在该类中,根据支付方式的类型来执行相应的操作。

public class PaymentProcessor {
    public void processPayment(Payment payment) {
        switch (payment.getType()) {
            case CREDIT_CARD:
                // 执行信用卡支付逻辑
                break;
            case PAYPAL:
                // 执行 PayPal 支付逻辑
                break;
            case BANK_TRANSFER:
                // 执行银行转账支付逻辑
                break;
            // 更多支付方式的分支
        }
    }
}
public interface Payment {
    void processPayment();
}

public class CreditCardPayment implements Payment {
    @Override
    public void processPayment() {
        // 执行信用卡支付逻辑
    }
}

public class PaypalPayment implements Payment {
    @Override
    public void processPayment() {
        // 执行 PayPal 支付逻辑
    }
}

public class BankTransferPayment implements Payment {
    @Override
    public void processPayment() {
        // 执行银行转账支付逻辑
    }
}

public class PaymentProcessor {
    public void processPayment(Payment payment) {
        payment.processPayment();
    }
}

解决方案:提取类

Parallel Inheritance Hierarchies(平行继承体系)

在一个系统中存在两个或多个相互关联的继承体系,这些继承体系中的类之间存在强耦合关系。以下是一个示例来说明平行继承体系的情况:

假设有一个图形编辑器的系统,其中有两个主要的继承体系:Shape(形状)和Color(颜色)。在Shape继承体系中有具体的形状类如Circle(圆形)和Rectangle(矩形),而在Color继承体系中有具体的颜色类如Red(红色)和Blue(蓝色)。这两个继承体系之间存在强耦合关系,需要相互配对使用。

// 形状继承体系
public abstract class Shape {
    protected Color color;

    // ...
}

public class Circle extends Shape {
    // ...
}

public class Rectangle extends Shape {
    // ...
}

// 颜色继承体系
public abstract class Color {
    // ...
}

public class Red extends Color {
    // ...
}

public class Blue extends Color {
    // ...
}

在这个例子中,Shape继承体系和Color继承体系是平行的,即每个具体的形状类需要与一个具体的颜色类配对使用。例如,Circle类需要与Red类配对使用,Rectangle类需要与Blue类配对使用。这种设计使得新增或修改形状或颜色时,需要同时修改两个继承体系中的类,导致代码的脆弱性和复杂性增加。

为了解决平行继承体系的问题,可以使用桥接模式(Bridge Pattern)来分离形状和颜色的继承关系,使它们可以独立变化。桥接模式通过将一个继承体系抽象为一个独立的继承层次结构,并通过组合关系将它与另一个继承体系关联起来。

// 形状继承体系
public abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    public abstract void draw();

    // ...
}

public class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        // 绘制圆形
        color.fill();
    }

    // ...
}

public class Rectangle extends Shape {
    public Rectangle(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        // 绘制矩形
        color.fill();
    }

    // ...
}

// 颜色继承体系
public interface Color {
    void fill();

    // ...
}

public class Red implements Color {
    @Override
    public void fill() {
        // 填充红色
    }

    // ...
}

public class Blue implements Color {
    @Override
    public void fill() {
        // 填充蓝色
    }

    // ...
}

通过桥接模式,Shape继承体系和Color继承体系被解耦,形状类和颜色类可以独立变化。每个具体的形状类通过组合关系持有一个颜色对象,并通过调用颜色对象的方法来实现绘制操作。这种设计减少了继承体系之间的耦合性,使系统更灵活、可扩展和易于维护。当新增或修改形状或颜色时,只需修改相应的类,不会对其他类造成影响。

Lazy Class (多余的类)

Speculative Generality(夸夸其谈未来性)

Temporary Field(令人迷糊的临时字段)

Message Chains(过度耦合的消息链)

在代码中存在过长的消息链式调用,即通过多个对象之间的连续方法调用来获取所需的数据或执行操作,导致代码的耦合度增加,可读性降低。以下是一个示例来说明过度耦合的消息链的情况:

假设有一个名为 Customer 的类,用于表示顾客信息。该类具有一个关联的 Order 对象,而 Order 类又关联了 Address 对象,以表示顾客的订单和地址信息。

javaCopy codepublic class Customer {
    private Order order;

    public Order getOrder() {
        return order;
    }
}

public class Order {
    private Address address;

    public Address getAddress() {
        return address;
    }
}

public class Address {
    private String street;
    private String city;
    private String country;

    public String getStreet() {
        return street;
    }

    public String getCity() {
        return city;
    }

    public String getCountry() {
        return country;
    }
}

在这个例子中,通过 Customer 对象获取顾客的国家信息可能需要经过多次连续的方法调用:

javaCopy codeCustomer customer = new Customer();
// ...

String country = customer.getOrder().getAddress().getCountry();

这样的代码存在过度耦合的消息链,使得代码的可读性和维护性降低。如果需要修改消息链中的任何一个类或方法,都会对调用链上的多个对象产生影响。

为了解决过度耦合的消息链的问题,可以考虑以下重构方式:

  1. 引入中间对象:通过引入中间对象来封装消息链,减少直接访问链式调用的次数。
javaCopy codepublic class Customer {
    private Order order;

    public Order getOrder() {
        return order;
    }

    public Address getAddress() {
        return order.getAddress();
    }
}

现在,获取顾客的国家信息可以通过中间对象 Customer 的方法来获取:

javaCopy codeCustomer customer = new Customer();
// ...

String country = customer.getAddress().getCountry();
  1. 将部分消息链提升为参数:如果只需要链中的部分数据,可以将需要的数据作为参数传递,避免直接访问整个消息链。
javaCopy codepublic class Customer {
    private Order order;

    public Order getOrder() {
        return order;
    }

    public String getCountry() {
        return order.getAddress().getCountry();
    }
}

现在,可以直接通过 Customer 对象获取顾客的国家信息:

javaCopy codeCustomer customer = new Customer();
// ...

String country = customer.getCountry();

通过以上重构方式,可以减少过度耦合的消息链的使用,提高代码的可读性和维护性。避免了对多个对象的连续方法调用,使代码更加清晰和易于理解。

Middle Man(多余的中间人)

存在一些中间类或中间方法,它们仅仅是转发调用到其他类或方法,对代码逻辑没有实质性的贡献。以下是一个示例来说明中间人的情况:

假设有一个名为 Employee 的类,表示公司的员工信息。而在公司的员工管理系统中,有一个 EmployeeService 类负责处理员工相关的业务逻辑。

javaCopy codepublic class Employee {
    private String name;
    private String department;

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }
}

public class EmployeeService {
    private List<Employee> employees;

    public List<Employee> getAllEmployees() {
        // 调用中间人方法获取员工列表
        return EmployeeRepository.getAllEmployees();
    }
}

public class EmployeeRepository {
    public static List<Employee> getAllEmployees() {
        // 实际的员工数据获取逻辑

        // ...
    }
}

在这个例子中,EmployeeService 类充当了中间人的角色,它的 getAllEmployees() 方法仅仅是转发调用到 EmployeeRepository 类的 getAllEmployees() 方法,没有添加任何额外的业务逻辑。

中间人的存在可能会导致代码的冗余和复杂性增加,不利于代码的维护和扩展。对于这种情况,可以考虑以下重构方式:

  1. 直接调用被调用对象:如果中间人仅仅是转发调用到其他类的方法,可以直接在调用方调用被调用对象的方法,避免引入不必要的中间人。
javaCopy codepublic class EmployeeService {
    private List<Employee> employees;

    public List<Employee> getAllEmployees() {
        // 直接调用被调用对象的方法
        return EmployeeRepository.getAllEmployees();
    }
}
  1. 移除中间人类:如果中间人类没有其他实质性的逻辑,可以直接在调用方直接调用被调用对象的方法,然后移除中间人类。
javaCopy codepublic class EmployeeService {
    private List<Employee> employees;

    public List<Employee> getAllEmployees() {
        // 直接调用被调用对象的方法
        // 移除中间人类

        return EmployeeRepository.getAllEmployees();
    }
}

通过以上重构方式,可以消除不必要的中间人,简化代码结构,提高代码的可读性和可维护性。避免了仅仅转发调用的冗余代码,使代码更加清晰和易于理解。

Inappropriate Intimacy(不恰当的亲密关系|狎昵关系)

两个类之间的关系过于密切,一个类过度依赖或访问另一个类的内部细节,违反了良好的封装原则。以下是一个示例来说明狎昵关系的情况:

假设有两个类 CustomerOrder,表示顾客和订单信息。现在,Customer 类需要获取 Order 类的一些信息,但是它过度地依赖和访问了 Order 类的内部细节。

javaCopy codepublic class Customer {
    private String name;
    private Order order;

    public String getName() {
        return name;
    }

    public String getOrderStatus() {
        // 过度依赖和访问Order类的内部细节
        if (order != null && order.getStatus() != null) {
            return order.getStatus();
        }
        return "Unknown";
    }
}

public class Order {
    private String status;

    public String getStatus() {
        return status;
    }
}

在这个例子中,Customer 类需要获取 Order 类的状态信息。但是,Customer 类直接访问了 Order 类的状态属性,违反了封装的原则。这种依赖关系过于密切,当 Order 类的内部结构发生变化时,需要修改 Customer 类的代码。

为了解决狎昵关系的问题,可以考虑以下重构方式:

  1. 将操作移到正确的类中:将获取订单状态的操作移动到 Order 类中,通过提供公共的访问方法来封装内部细节。
javaCopy codepublic class Customer {
    private String name;
    private Order order;

    public String getName() {
        return name;
    }

    public String getOrderStatus() {
        if (order != null) {
            return order.getStatus();
        }
        return "Unknown";
    }
}

public class Order {
    private String status;

    public String getStatus() {
        return status;
    }
    
    // 其他Order类的操作和属性
}

现在,Customer 类只需要调用 Order 类的公共方法来获取订单状态,而不直接依赖和访问内部细节。

  1. 使用委托:如果 Customer 类需要更多的订单操作,可以通过委托的方式将操作交给 Order 类来处理。
javaCopy codepublic class Customer {
    private String name;
    private Order order;

    public String getName() {
        return name;
    }

    public String getOrderStatus() {
        if (order != null) {
            return order.getStatus();
        }
        return "Unknown";
    }
    
    public void cancelOrder() {
        if (order != null) {
            order.cancel();
        }
    }
}

public class Order {
    private String status;

    public String getStatus() {
        return status;
    }
    
    public void cancel() {
        // 取消订单的操作
    }
    
    // 其他Order类的操作和属性
}

通过委托的方式,Customer 类将订单操作委托给 Order 类进行处理,减少了类之间的耦合性,提高了代码的可维护性和灵活性。

通过以上重构方式,可以解决狎昵关系带来的问题,减少类之间的依赖,增强代码的封装性和可扩展性。

Alternative Classes with Different Interfaces(异曲同工的类)

存在两个或多个类,它们实现了相似的功能,但使用了不同的接口或方法命名,导致代码的可读性和维护性降低。以下是一个示例来说明异曲同工的类的情况:

假设有两个类 RectangleSquare,表示矩形和正方形。它们都有计算面积和周长的方法,但使用了不同的接口或方法命名。

javaCopy codepublic class Rectangle {
    private int width;
    private int height;

    public int calculateArea() {
        return width * height;
    }

    public int calculatePerimeter() {
        return 2 * (width + height);
    }
}

public class Square {
    private int sideLength;

    public int calculateSquareArea() {
        return sideLength * sideLength;
    }

    public int calculateSquarePerimeter() {
        return 4 * sideLength;
    }
}

在这个例子中,Rectangle 类和 Square 类都表示不同的几何形状,但是它们的功能是相似的:计算面积和周长。然而,它们使用了不同的方法命名和接口,使得代码阅读和维护变得困难。

为了解决异曲同工的类的问题,可以考虑以下重构方式:

  1. 统一接口和方法命名:将类的接口和方法命名统一,使得功能相似的类使用相同的接口和方法。
javaCopy codepublic interface Shape {
    int calculateArea();

    int calculatePerimeter();
}

public class Rectangle implements Shape {
    private int width;
    private int height;

    @Override
    public int calculateArea() {
        return width * height;
    }

    @Override
    public int calculatePerimeter() {
        return 2 * (width + height);
    }
}

public class Square implements Shape {
    private int sideLength;

    @Override
    public int calculateArea() {
        return sideLength * sideLength;
    }

    @Override
    public int calculatePerimeter() {
        return 4 * sideLength;
    }
}

通过定义一个统一的接口 Shape,将功能相似的类统一到同一个接口下,并使用相同的方法命名,提高了代码的可读性和维护性。

  1. 继承关系:如果存在一些共同的属性和方法,可以考虑使用继承来消除重复代码。
javaCopy codepublic abstract class Shape {
    // 公共属性和方法

    public abstract int calculateArea();

    public abstract int calculatePerimeter();
}

public class Rectangle extends Shape {
    private int width;
    private int height;

    @Override
    public int calculateArea() {
        return width * height;
    }

    @Override
    public int calculatePerimeter() {
        return 2 * (width + height);
    }
}

public class Square extends Shape {
    private int sideLength;

    @Override
    public int calculateArea()

Incomplete Library Class(不完整的库类)

库或框架中的某个类功能不完整,缺少某些常用的方法或功能,导致开发者需要自行补充或扩展该类的功能。以下是一个示例来说明不完整的库类的情况:

假设我们使用一个第三方库中的 StringUtils 类来处理字符串相关操作。然而,该类缺少一些常用的方法,比如截取子串、替换字符串等。

javaCopy codepublic class StringUtils {
    // 该类缺少一些常用的方法

    public static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }

    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }
}

在这个例子中,StringUtils 类是一个常用的字符串处理类,但是它缺少一些常用的方法,限制了开发者在处理字符串时的灵活性。为了补充缺失的功能,开发者可能需要自行编写或扩展该类的功能,增加了额外的工作量和复杂度。

为了解决不完整的库类的问题,可以考虑以下重构方式:

  1. 扩展库类:创建一个新的类,继承库类并添加缺失的方法或功能。
javaCopy codepublic class EnhancedStringUtils extends StringUtils {
    public static String substring(String str, int startIndex, int endIndex) {
        // 添加截取子串的功能
        if (str == null || startIndex < 0 || endIndex < 0 || startIndex >= str.length() || endIndex > str.length()) {
            throw new IllegalArgumentException("Invalid arguments");
        }
        return str.substring(startIndex, endIndex);
    }

    public static String replace(String str, String oldSubstring, String newSubstring) {
        // 添加替换字符串的功能
        if (str == null || oldSubstring == null || newSubstring == null) {
            throw new IllegalArgumentException("Invalid arguments");
        }
        return str.replace(oldSubstring, newSubstring);
    }
}

通过创建一个新的类 EnhancedStringUtils,继承原始的 StringUtils 类并添加缺失的方法或功能,可以补充库类的不完整功能,提供更全面的字符串处理功能。

  1. 自定义工具类:如果需要的功能较多或更复杂,可以考虑创建一个自定义的工具类,提供完整的功能集合。
javaCopy codepublic class CustomStringUtils {
    public static String substring(String str, int startIndex, int endIndex) {
        // 添加截取子串的功能
        if (str == null || startIndex < 0 || endIndex < 0 || startIndex >= str.length() || endIndex > str.length()) {
            throw new IllegalArgumentException("Invalid arguments");
        }
        return str.substring(startIndex, endIndex);
    }

    public static String replace(String str, String oldSubstring, String newSubstring) {
        // 添加替换字符串的功能
        if (str == null || oldSubstring == null || newSubstring == null) {
           

Data Class(数据类)

Refused Bequest(被拒绝的馈赠)

Comments(过多的注释)

  • 57
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
第1章 重构,第一个案例 1 1.1 起点 1 1.2 重构的第一步 7 1.3 分解并重组statement() 8 1.4 运用多态取代与价格相关的条件逻辑 34 1.5 结语 52 第2章 重构原则 53 2.1 何谓重构 53 2.2 为何重构 55 2.3 何时重构 57 2.4 怎么对经理说 60 2.5 重构的难题 62 2.6 重构设计 66 2.7 重构与性能 69 2.8 重构起源何处 71 第3章 代码味道 75 3.1 Duplicated Code(重复代码) 76 3.2 Long Method(过长函数) 76 3.3 Large Class(过大的类) 78 3.4 Long Parameter List(过长参数列) 78 3.5 Divergent Change(发散式变化) 79 3.6 Shotgun Surgery(霰弹式修改) 80 3.7 Feature Envy(依恋情结) 80 3.8 Data Clumps(数据泥团) 81 3.9 Primitive Obsession(基本类型偏执) 81 3.10 Switch Statements(switch惊悚现身) 82 3.11 Parallel InheritanceHierarchies(平行继承体系) 83 3.12 Lazy Class(冗赘类) 83 3.13 Speculative Generality(夸夸其谈未来性) 83 3.14 Temporary Field(令人迷惑的暂时字段) 84 3.15 Message Chains(过度耦合的消息链) 84 3.16 Middle Man(中间人) 85 3.17 Inappropriate Intimacy(狎昵关系) 85 3.18 Alternative Classes with Different Interfaces(异曲同工的类) 85 3.19 Incomplete Library Class(不完美的库类) 86 3.20 Data Class(纯稚的数据类) 86 3.21 Refused Bequest(被拒绝的遗赠) 87 3.22 Comments(过多的注释) 87 第4章 构筑测试体系 89 4.1 自测试代码的价值 89 4.2 JUnit测试框架 91 4.3 添加更多测试 97 第5章 重构列表 103 5.1 重构的记录格式 103 5.2 寻找引用点 105 5.3 这些重构手法有多成熟 106 第6章 重新组织函数 109 6.1 Extract Method(提炼函数) 110 6.2 Inline Method(内联函数) 117 6.3 Inline Temp(内联临时变量) 119 6.4 Replace Temp with Query(以查询取代临时变量) 120 6.5 Introduce Explaining Variable(引入解释性变量) 124 6.6 Split Temporary Variable(分解临时变量) 128 6.7 Remove Assignments to Parameters(移除对参数的赋值) 131 6.8 Replace Method with Method Object(以函数对象取代函数) 135 6.9 Substitute Algorithm(替换算法) 139 第7章 在对象之间搬移特性 141 7.1 Move Method(搬移函数) 142 7.2 Move Field(搬移字段) 146 7.3 Extract Class(提炼类) 149 7.4 Inline Class(将类内联化) 154 7.5 Hide Delegate(隐藏“委托关系”) 157 7.6 Remove Middle Man(移除中间人) 160 7.7 Introduce Foreign Method(引入外加函数) 162 7.8 Introduce Local Extension(引入本地扩展) 164 第8章 重新组织数据 169 8.1 Self Encapsulate Field(自封装字段) 171 8.2 Replace Data Value with Object(以对象取代数据值) 175 8.3 Change Value to Reference(将值对象改为引用对象) 179 8.4 Change Reference to Value(将引用对象改为值对象) 183 8.5 Replace Array with Object(以对象取代数组) 186 8.6 Duplicate Observed Data(复制“被监视数据”) 189 8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向关联) 197 8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向关联) 200 8.9 Replace Magic Number with Symbolic Constant(以字面常量取代魔法数) 204 8.10 Encapsulate Field(封装字段) 206 8.11 Encapsulate Collection(封装集合) 208 8.12 Replace Record with Data Class(以数据类取代记录) 217 8.13 Replace Type Code with Class(以类取代类型码) 218 8.14 Replace Type Code with Subclasses(以子类取代类型码) 223 8.15 Replace Type Code with State/Strategy(以State/Strategy取代类型码) 227 8.16 Replace Subclass with Fields(以字段取代子类) 232 第9章 简化条件表达式 237 9.1 Decompose Conditional(分解条件表达式) 238 9.2 Consolidate Conditional Expression(合并条件表达式) 240 9.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段) 243 9.4 Remove Control Flag(移除控制标记) 245 9.5 Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式) 250 9.6 Replace Conditional with Polymorphism(以多态取代条件表达式) 255 9.7 Introduce Null Object(引入Null对象) 260 9.8 Introduce Assertion(引入断言) 267 第10章 简化函数调用 271 10.1 Rename Method(函数改名) 273 10.2 Add Parameter(添加参数) 275 10.3 Remove Parameter(移除参数) 277 10.4 Separate Query from Modifier(将查询函数和修改函数分离) 279 10.5 Parameterize Method(令函数携带参数) 283 10.6 Replace Parameter with Explicit Methods(以明确函数取代参数) 285 10.7 Preserve Whole Object(保持对象完整) 288 10.8 Replace Parameter with Methods(以函数取代参数) 292 10.9 Introduce Parameter Object(引入参数对象) 295 10.10 Remove Setting Method(移除设值函数) 300 10.11 Hide Method(隐藏函数) 303 10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数) 304 10.13 Encapsulate Downcast(封装向下转型) 308 10.14 Replace Error Code with Exception(以异常取代错误码) 310 10.15 Replace Exception with Test(以测试取代异常) 315 第11章 处理概括关系 319 11.1 Pull Up Field(字段上移) 320 11.2 Pull Up Method(函数上移) 322 11.3 Pull Up Constructor Body(构造函数本体上移) 325 11.4 Push Down Method(函数下移) 328 11.5 Push Down Field(字段下移) 329 11.6 Extract Subclass(提炼子类) 330 11.7 Extract Superclass(提炼超类) 336 11.8 Extract Interface(提炼接口) 341 11.9 Collapse Hierarchy(折叠继承体系) 344 11.10 Form Tem Plate Method(塑造模板函数) 345 11.11 Replace Inheritance with Delegation(以委托取代继承) 352 11.12 Replace Delegation with Inheritance(以继承取代委托) 355 第12章 大型重构 359 12.1 Tease Apart Inheritance(梳理并分解继承体系) 362 12.2 Convert Procedural Design to Objects(将过程化设计转化为对象设计) 368 12.3 Separate Domain from Presentation(将领域和表述/显示分离) 370 12.4 Extract Hierarchy(提炼继承体系) 375 第13章 重构,复用与现实 379 13.1 现实的检验 380 13.2 为什么开发者不愿意重构他们的程序 381 13.3 再论现实的检验 394 13.4 重构的资源和参考资料 394 13.5 从重构联想到软件复用和技术传播 395 13.6 小结 397 13.7 参考文献 397 第14章 重构工具 401 14.1 使用工具进行重构 401 14.2 重构工具的技术标准 403 14.3 重构工具的实用标准 405 14.4 小结 407 第15章 总结 409

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值