Self Encapsulate Field (自封装字段)

它的主要目的是通过使用 getter 和 setter 方法来访问类的私有属性,以提高代码的可维护性和灵活性。

举个例子,假设有一个名为 Customer 的类,用于表示一个客户对象,该类包含了客户的姓名、地址等信息。现在需要在该类中添加一个新的属性,即客户的信用度 credit,但同时要限制信用度的范围在 0 到 100 之间,并且在修改信用度时要触发相应的事件通知。

首先可以将信用度属性设置为私有属性,然后使用 Self Encapsulate Field 重构,在该类中添加一个 getter 和 setter 方法来访问该属性,并在 setter 方法中添加相应的限制条件和事件通知逻辑。

public class Customer {
    private String name;
    private String address;
    private int credit;
    
    // getter and setter methods for name, address
    
    public int getCredit() {
        return credit;
    }
    
    public void setCredit(int credit) {
        if (credit < 0) {
            credit = 0;
        } else if (credit > 100) {
            credit = 100;
        }
        
        this.credit = credit;
        notifyCreditChanged();
    }
    
    private void notifyCreditChanged() {
        // send notification to interested parties
    }
}

在上述代码中,将信用度属性设置为私有属性,然后添加了一个名为 getCredit() 的 getter 方法和一个名为 setCredit() 的 setter 方法。在 setter 方法中,对信用度进行了范围限制,并在修改属性值后触发了一个名为 notifyCreditChanged() 的事件通知方法。

客户端可以通过调用 getCredit() 和 setCredit() 方法来获取和设置客户的信用度,这样就可以保证在任何时候都能够对信用度进行有效的控制和管理。

Customer customer = new Customer();
// set customer's name, address
customer.setCredit(80);
int credit = customer.getCredit();

通过使用 Self Encapsulate Field 重构,成功将信用度属性进行了封装,并且添加了相应的控制和管理逻辑,同时也保留了原有 Customer 类的功能和行为。需要注意的是,在使用 Self Encapsulate Field 重构时,要确保 getter 和 setter 方法的实现是正确和合理的,并且考虑到类的封装性和保护性等因素,确保程序的整体结构和功能不会发生变化。

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

它的主要目的是将一个或多个相关的数据值封装成一个对象来实现更灵活、更可扩展、更易维护的代码结构。

举个例子,假设有一个名为 Order 的类,用于表示一个订单对象,该类包含了订单号、订单日期、客户信息等数据。其中客户信息由客户名称和地址组成,现在需要增加一个新的属性,即客户电话。可以考虑使用 Replace Data Value with Object 重构,将客户信息封装成客户对象,使其更容易进行管理和扩展。

首先可以定义一个名为 Customer 的类,用于表示客户对象,该类包含了客户名称、地址和电话号码等信息,然后在 Order 类中将客户信息改为一个指向 Customer 对象的引用。

public class Customer {
    private String name;
    private String address;
    private String phone;
    
    // getter and setter methods for name, address, phone
}

public class Order {
    private int orderId;
    private Date orderDate;
    private Customer customer;
    
    // getter and setter methods for orderId, orderDate
    
    public Customer getCustomer() {
        return customer;
    }
    
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
}

在上述代码中,定义了一个名为 Customer 的类,用于表示客户对象,并在 Order 类中将客户信息改为一个指向 Customer 对象的引用。客户端可以通过创建 Customer 对象,并将其作为参数传入 Order 对象来进行操作。

Customer customer = new Customer();
// set customer's name, address, phone
Order order = new Order();
// set order's orderId, orderDate
order.setCustomer(customer);

通过使用 Replace Data Value with Object 重构,成功将客户信息封装成客户对象,并且添加了相应的控制和管理逻辑,同时也保留了原有 Order 类的功能和行为。需要注意的是,在使用 Replace Data Value with Object 重构时,要确保新定义的对象能够满足需求,同时也要考虑程序的依赖关系和设计初衷等因素,确保程序的整体结构和功能不会发生变化。

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

它的主要目的是将一个或多个相同的值对象封装成共享的、可共享的引用对象来实现更灵活、更可扩展、更节省内存的代码结构。

举个例子,假设有一个名为 Employee 的类,用于表示一个员工对象,该类包含了员工编号、员工姓名、员工部门等数据。其中员工部门是一个值对象,由部门名称和部门经理等属性组成,现在需要增加一个新的属性,即部门电话。可以考虑使用 Change Value to Reference 重构,将部门信息封装成共享的引用对象 Department,以提高代码的可重用性和内存使用效率。

首先可以定义一个名为 Department 的类,用于表示部门对象,该类包含了部门名称、部门经理和部门电话等信息,然后在 Employee 类中将部门信息改为一个指向 Department 对象的引用。

public class Department {
    private String name;
    private String manager;
    private String phone;
    
    // getter and setter methods for name, manager, phone
}

public class Employee {
    private int empId;
    private String empName;
    private Department department;
    
    // getter and setter methods for empId, empName
    
    public Department getDepartment() {
        return department;
    }
    
    public void setDepartment(Department department) {
        this.department = department;
    }
}

在上述代码中,定义了一个名为 Department 的类,用于表示部门对象,并在 Employee 类中将部门信息改为一个指向 Department 对象的引用。客户端可以通过创建 Department 对象,并将其作为参数传入 Employee 对象来进行操作。

Department department = new Department();
// set department's name, manager, phone
Employee employee = new Employee();
// set employee's empId, empName
employee.setDepartment(department);

通过使用 Change Value to Reference 重构,成功将部门信息封装成共享的引用对象 Department,并且添加了相应的控制和管理逻辑,同时也保留了原有 Employee 类的功能和行为。需要注意的是,在使用 Change Value to Reference 重构时,要确保新定义的对象能够满足需求,并且具有良好的可维护性和扩展性等特点,确保程序的整体结构和功能不会发生变化。

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

它的主要目的是将一个或多个相关的引用对象转换成值对象,以提高代码的可维护性和性能。

举个例子,假设有一个名为 Customer 的类,用于表示一个客户对象,该类包含了客户名称、客户地址和电话号码等属性。现在需要将客户地址改为值对象,即 Address 类,由于客户地址可能存在多个,如果每个地址都作为独立的对象存储,则会浪费大量的内存空间。可以考虑使用 Change Reference to Value 重构,将地址信息封装成值对象 Address,以提高代码的性能和可维护性。

首先可以定义一个名为 Address 的类,用于表示地址对象,该类包含了省份、城市和详细地址等信息。然后在 Customer 类中将地址信息改为 Address 对象的属性。

public class Address {
    private String province;
    private String city;
    private String detail;

    // getter and setter methods for province, city, detail
}

public class Customer {
    private String name;
    private Address address;
    private String phone;

    // getter and setter methods for name, phone

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

在上述代码中,定义了一个名为 Address 的类,用于表示地址对象,并在 Customer 类中将地址信息改为 Address 对象的属性。客户端可以通过创建 Address 对象,并将其作为参数传入 Customer 对象来进行操作。

Address address = new Address();
// set address's province, city, detail
Customer customer = new Customer();
// set customer's name, phone
customer.setAddress(address);

通过使用 Change Reference to Value 重构,将引用对象 Address 转换成值对象 Address,成功优化了内存空间的使用,同时也保留了原有 Customer 类的功能和行为。需要注意的是,在使用 Change Reference to Value 重构时,要确保新定义的对象能够满足需求,并且将对象转换成值对象不会影响程序的逻辑和功能。

Replace Array with Object(以对象取代数组)

它的主要目的是通过创建一个新的对象来取代既有的数组结构,从而提高代码的可维护性和可扩展性。

举个例子,假设有一个名为 Students 的类,用于表示一个学生对象数组,该类包含了学生姓名、学号、成绩等属性。现在需要增加一个新的属性,即学生所在的班级,可以考虑使用 Replace Array with Object 重构,将学生信息封装成一个 Student 类对象,以实现更灵活、更可维护的代码结构。

首先可以定义一个名为 Student 的类,用于表示学生对象,该类包含了学生姓名、学号、班级和成绩等信息。然后在 Students 类中将学生信息改为 Student 对象的数组。

public class Student {
    private String name;
    private int id;
    private String className;
    private double score;

    // getter and setter methods for name, id, className, score
}

public class Students {
    private Student[] students;

    // getter and setter methods for students
}

在上述代码中,定义了一个名为 Student 的类,用于表示学生对象,并在 Students 类中将学生信息改为 Student 对象的数组。客户端可以通过创建 Student 对象,并将其作为参数传入 Students 对象来进行操作。

Student student1 = new Student();
// set student1's name, id, className, score
Student student2 = new Student();
// set student2's name, id, className, score
Students students = new Students();
students.setStudents(new Student[] {student1, student2});

通过使用 Replace Array with Object 重构,将学生信息封装成一个 Student 类对象,成功实现了更灵活、更可维护的代码结构。需要注意的是,在使用 Replace Array with Object 重构时,要确保新定义的对象能够满足需求,并且具有良好的可维护性和扩展性等特点,确保程序的整体结构和功能不会发生变化。

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

它的主要目的是消除两个或多个不同对象之间共享的、可观察的数据项,从而简化程序的逻辑和实现。

举个例子,假设有两个名为 BankAccount 和 ATM 的类,用于表示银行账户对象和自动取款机对象,两个类中都包含了余额 balance 属性。现在需要在两个类之间同步余额信息,可以考虑使用 Duplicate Observed Data 重构,将被监视数据余额复制到一个新的类中,以消除两个类之间数据的重复和依赖。

首先可以定义一个名为 AccountBalance 的类,用于表示余额信息,该类包含了余额 balance 属性。然后在 BankAccount 和 ATM 类中使用 AccountBalance 类对象来存储和同步余额信息。

public class AccountBalance {
    private double balance;

    // getter and setter methods for balance
}

public class BankAccount {
    private AccountBalance accountBalance;

    // getter and setter methods for accountBalance
}

public class ATM {
    private AccountBalance accountBalance;

    // getter and setter methods for accountBalance
}

在上述代码中,定义了一个名为 AccountBalance 的类,用于表示余额信息,并在 BankAccount 和 ATM 类中使用 AccountBalance 类对象来存储和同步余额信息。余额信息只存在于 AccountBalance 类对象中,BankAccount 和 ATM 类通过对 AccountBalance 类对象的引用来获取和更新余额信息。

AccountBalance accountBalance = new AccountBalance();
// set accountBalance's balance
BankAccount bankAccount = new BankAccount();
bankAccount.setAccountBalance(accountBalance);
ATM atm = new ATM();
atm.setAccountBalance(accountBalance);

通过使用 Duplicate Observed Data 重构,成功消除了两个类之间数据的重复和依赖,实现了更简单、更清晰的设计和实现。需要注意的是,在使用 Duplicate Observed Data 重构时,要确保被复制的数据能够满足需求,并且复制操作不会影响程序的逻辑和功能。

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

它的主要目的是增加双向关联,提高程序的灵活性和可维护性。

举个例子,假设有两个名为 Customer 和 Order 的类,用于表示客户对象和订单对象,Customer 类中包含了一个 Order 类型的属性 orders,表示该客户所拥有的所有订单。现在需要在 Order 类中访问客户信息,可以考虑使用 Change Unidirectional Association to Bidirectional 重构,将单向关联改为双向关联,以便更方便地访问客户信息。

首先可以在 Order 类中添加一个 Customer 类型的属性 customer,并在 Customer 类中添加一个 List 类型的属性 orders,表示该客户所拥有的所有订单。然后在双方类中实现各自的 setter 方法,确保双向关联能够正确地维护。

public class Customer {
    private List<Order> orders = new ArrayList<>();

    public void addOrder(Order order) {
        orders.add(order);
        order.setCustomer(this);
    }

    // getter and setter methods for orders
}

public class Order {
    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
        customer.getOrders().add(this);
    }
}

在上述代码中,定义了一个 Customer 类和一个 Order 类,使用 Change Unidirectional Association to Bidirectional 重构将单向关联改为双向关联。在 Customer 类中添加了一个 List 类型的属性 orders,表示该客户所拥有的所有订单,并实现了 addOrder 方法,在添加订单时同时将订单的 customer 属性设置为当前客户对象。在 Order 类中添加了一个 Customer 类型的属性 customer,并实现了 setCustomer 方法,在设置订单的 customer 属性时同时将订单加入到客户的 orders 属性中。

Customer customer = new Customer();
Order order = new Order();
customer.addOrder(order);

通过使用 Change Unidirectional Association to Bidirectional 重构,成功增加了双向关联,实现了更方便、更灵活的访问客户信息。需要注意的是,在使用 Change Unidirectional Association to Bidirectional 重构时,要确保新的双向关联能够正确地维护,防止出现不一致或死循环等问题。

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

它的主要目的是消除类之间的双向依赖,降低程序的复杂度和耦合度。

举个例子,假设有两个名为 Customer 和 Order 的类,用于表示客户对象和订单对象,Customer 类中包含了一个 List 类型的属性 orders,表示该客户所拥有的所有订单;而 Order 类中包含了一个 Customer 类型的属性 customer,表示该订单所属的客户。现在需要消除双向依赖,可以考虑使用 Change Bidirectional Association to Unidirectional 重构,将双向关联改为单向关联,以简化程序的逻辑和实现。

首先可以在 Order 类中删除 Customer 类型的属性 customer,并在 Customer 类中添加一个方法 removeOrder,用于从当前客户的 orders 属性中删除指定订单。然后在 Order 类中添加一个 getCustomer 方法,根据订单的信息获取相应的客户对象。

public class Customer {
    private List<Order> orders = new ArrayList<>();

    public void addOrder(Order order) {
        orders.add(order);
    }

    public void removeOrder(Order order) {
        orders.remove(order);
    }

    // getter and setter methods for orders
}

public class Order {
    private int customerId;

    public int getCustomerId() {
        return customerId;
    }

    // setter method for customerId

    public Customer getCustomer(List<Customer> customers) {
        for (Customer customer : customers) {
            if (customer.getOrders().contains(this)) {
                return customer;
            }
        }
        return null;
    }
}

在上述代码中,定义了一个 Customer 类和一个 Order 类,使用 Change Bidirectional Association to Unidirectional 重构将双向关联改为单向关联。在 Customer 类中删除了 Order 类型的属性 customer,并实现了 removeOrder 方法,用于从当前客户的 orders 属性中删除指定订单。在 Order 类中添加了一个 int 类型的属性 customerId,并实现了 getCustomer 方法,在订单的信息中获取 customerId 属性的值后根据该值遍历所有客户对象,查找包含当前订单的客户对象并返回。

Customer customer = new Customer();
Order order = new Order();
customer.addOrder(order);
// set order's customerId

List<Customer> customers = new ArrayList<>();
customers.add(customer);

Customer orderCustomer = order.getCustomer(customers);

通过使用 Change Bidirectional Association to Unidirectional 重构,成功消除了类之间的双向依赖,实现了更简单、更清晰的逻辑和实现。需要注意的是,在使用 Change Bidirectional Association to Unidirectional 重构时,要确保单向关联能够满足需求,并且不会影响程序的功能和性能。

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

它的主要目的是通过定义常量来代替代码中散乱分布的数字,提高程序的可读性和可维护性。

举个例子,假设有一个名为 Circle 的类用于表示圆形对象,其中包含了一个名为 calculateArea 的方法,用于计算圆形的面积。在该方法的实现中,出现了一个数字 3.14,表示圆周率的值。为了提高程序的可读性和可维护性,可以考虑使用 Replace Magic Number with Symbolic Constant 重构,将数字 3.14 定义为一个常量,并在程序中使用该常量代替数字。

首先可以在 Circle 类中添加一个静态常量 PI,用于表示圆周率的值。然后在 calculateArea 方法的实现中使用该常量代替数字 3.14,使得代码更具可读性。

public class Circle {
    private double radius;
    public static final double PI = 3.14;

    public double calculateArea() {
        return PI * radius * radius;
    }

    // getter and setter methods for radius
}

在上述代码中,定义了一个 Circle 类,使用 Replace Magic Number with Symbolic Constant 重构,将数字 3.14 定义为一个静态常量 PI,并在 calculateArea 方法的实现中使用该常量代替数字,使得代码更易读、更易于维护。

Circle circle = new Circle();
circle.setRadius(10.0);
double area = circle.calculateArea();

通过使用 Replace Magic Number with Symbolic Constant 重构,成功将数字 3.14 定义为常量 PI,并在程序中使用该常量代替数字,提高了程序的可读性和可维护性。需要注意的是,在使用 Replace Magic Number with Symbolic Constant 重构时,应该对常量的命名进行慎重考虑,选择与业务相关且易于理解的名称,从而更好地表达代码的意图。

Encapsulate Field (封装字段)

它的主要目的是将类中公共的属性封装起来,通过添加相应的 getter 和 setter 方法,控制访问该属性的方式,从而实现更好的封装性和安全性。

举个例子,假设有一个名为 Student 的类用于表示学生对象,其中包含了一个公共属性 name,表示学生的姓名。为了实现更好的封装性和安全性,可以考虑使用 Encapsulate Field 重构,将公共属性 name 封装起来,从而只允许通过 getter 和 setter 方法来访问该属性。

首先可以在 Student 类中添加私有属性 name,然后在该类中添加公共的 getter 和 setter 方法,用于获取和设置学生的姓名。由于该属性已经被封装起来,因此只有通过相应的方法才能访问该属性,从而实现更好的封装性和安全性。

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在上述代码中,定义了一个 Student 类,使用 Encapsulate Field 重构,将公共属性 name 封装起来,并分别添加了一个公共的 getter 和 setter 方法,用于获取和设置学生的姓名。由于该属性已经被封装起来,因此在程序中只能通过这些方法来访问该属性,从而更好地实现了封装性和安全性。

Student student = new Student();
student.setName("Tom");
String name = student.getName();

通过使用 Encapsulate Field 重构,成功将公共属性 name 封装起来,并控制了对该属性的访问方式,从而实现更好的封装性和安全性。需要注意的是,在使用 Encapsulate Field 重构时,应该根据属性的特点和业务场景,选择合适的访问控制级别,从而更好地保护属性的安全性。

Encapsulate Collection(封装集合)

它的主要目的是将集合类型的属性封装起来,并通过添加相应的方法来控制对集合的访问方式,从而实现更好的封装性和安全性。

举个例子,假设有一个名为 School 的类用于表示学校对象,其中包含了一个公共属性 students,表示学校所有学生的集合。为了实现更好的封装性和安全性,可以考虑使用 Encapsulate Collection 重构,将公共属性 students 封装起来,从而只允许通过特定的方法来访问该集合。

首先可以在 School 类中添加私有属性 students,代表学生集合。然后在该类中添加一些公共的方法,例如 addStudent 和 removeStudent,用于向集合中添加或删除学生,并添加一个公共的方法 getStudents,用于获取学校的所有学生。由于该集合已经被封装起来,因此只有通过相应的方法才能访问该集合,从而实现更好的封装性和安全性。

public class School {
    private List<Student> students = new ArrayList<>();

    public void addStudent(Student student) {
        students.add(student);
    }

    public void removeStudent(Student student) {
        students.remove(student);
    }

    public List<Student> getStudents() {
        return Collections.unmodifiableList(students);
    }
}

在上述代码中,定义了一个 School 类,使用 Encapsulate Collection 重构,将公共属性 students 封装起来,并添加了一些公共的方法,用于访问该集合。由于该集合已经被封装起来,因此在程序中只能通过这些方法来访问该集合,从而更好地实现了封装性和安全性。需要注意的是,对于 getStudents 方法,使用了 Collections.unmodifiableList 方法返回一个不可修改的 List,以避免外部代码对集合进行非法操作。

School school = new School();
Student student1 = new Student("Tom");
Student student2 = new Student("Jerry");

school.addStudent(student1);
school.addStudent(student2);

List<Student> students = school.getStudents();

通过使用 Encapsulate Collection 重构,成功将公共属性 students 封装起来,并控制了对集合的访问方式,从而实现更好的封装性和安全性。需要注意的是,在使用 Encapsulate Collection 重构时,应该根据集合的特点和业务场景,选择合适的方法来控制集合的访问、修改方式,以保证程序的安全性和正确性。

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

它的主要目的是将一个记录(Record)类型的类替换为一组简单的数据类(Data Class),从而简化代码并提高代码的可读性。

举个例子,假设有一个名为 Person 的类用于表示人员信息,其中包含了一组公共属性,例如姓名、年龄、地址等。为了简化代码并提高代码的可读性,可以考虑使用 Replace Record with Data Class 重构,将该类替换为一组简单的数据类,从而去除了所有的行为方法,只保留了数据属性。

首先,可以创建一个名为 PersonData 的数据类,包含与原来的 Person 类相同的数据属性,并添加适当的构造函数和 getter/setter 方法。

public class PersonData {
    private String name;
    private int age;
    private String address;

    public PersonData(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

接下来,在程序中使用 PersonData 类来替换原来的 Person 类。由于 PersonData 类只包含属性,因此可以更容易地理解数据结构,并且不需要关注与行为相关的复杂性。

PersonData person = new PersonData("Tom", 25, "Beijing");
String name = person.getName();

通过使用 Replace Record with Data Class 重构,成功将原来的 Person 类替换为了一个简单的数据类 PersonData,去掉了与行为相关的复杂性,使代码更加简洁和易于理解。需要注意的是,在使用该重构方法时,应仔细考虑每个属性的特点和意义,以确保新的数据类具有准确性和完整性,同时还需要考虑向后兼容性。

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

它的主要目的是将一个类型码的属性替换为一个具体的类,从而提高代码的可读性和可维护性。

举个例子,假设有一个名为 Employee 的类用于表示雇员信息,其中包含了一个公共属性 typeCode,表示雇员类型的代码。为了提高代码的可读性和可维护性,可以考虑使用 Replace Type Code with Class 重构,将 typeCode 属性替换为具体的类,例如 Engineer、Salesman 和 Manager。

首先,可以创建三个具体的类来分别代表不同的雇员类型,并在这些类中定义各自特有的属性和行为方法。然后,在 Employee 类中移除 typeCode 属性,并添加一个代表雇员类型的抽象类 EmployeeType,该类有三个具体的子类,分别为 Engineer、Salesman 和 Manager。Employee 类只包含一个引用 EmployeeType 的属性,并在其构造函数中获取相应的员工类型。

public abstract class EmployeeType {
    public abstract int getTypeCode();
}

public class Engineer extends EmployeeType {
    public int getTypeCode() {
        return 0;
    }
}

public class Salesman extends EmployeeType {
    public int getTypeCode() {
        return 1;
    }
}

public class Manager extends EmployeeType {
    public int getTypeCode() {
        return 2;
    }
}

public class Employee {
    private int monthlySalary;
    private int commission;
    private int bonus;
    private EmployeeType type;

    public Employee(int monthlySalary, int commission, int bonus, EmployeeType type) {
        this.monthlySalary = monthlySalary;
        this.commission = commission;
        this.bonus = bonus;
        this.type = type;
    }

    public int getTypeCode() {
        return type.getTypeCode();
    }
}

接下来,在程序中使用 Employee 类来表示雇员信息。由于每个雇员都有具体的类型,因此 Employee 类不再需要维护 typeCode 属性,而是将其替换为一个具体的类型类。同时,由于每个类型的特点和行为不同,因此可以更好地封装各个类型的属性和行为方法。

Employee employee = new Employee(1000, 500, 100, new Engineer());
int typeCode = employee.getTypeCode();

通过使用 Replace Type Code with Class 重构,成功将原来的 typeCode 属性替换为具体的类,提高了代码的可读性和可维护性,同时也增强了对各种类型的封装性和灵活性。需要注意的是,在使用该重构方法时,应仔细考虑每个类型的特点和意义,以确保新的类具有准确性和完整性,并适当地处理向后兼容性。

Replace Type Code with Subclasses(以子类取代类型码)

它的主要目的是将一个类型码的类替换为它的子类,从而提高代码的可读性和可维护性。

举个例子,假设有一个名为 Employee 的类用于表示雇员信息,其中包含了一个公共属性 typeCode,表示雇员类型的代码。为了提高代码的可读性和可维护性,可以考虑使用 Replace Type Code with Subclasses 重构,将 typeCode 属性替换为具体的子类,例如 Engineer、Salesman 和 Manager。

首先,可以创建三个具体的子类分别代表不同的雇员类型,并在这些子类中定义各自特有的属性和行为方法。然后,在 Employee 类中移除 typeCode 属性,并将其替换为由具体的子类来代表雇员类型。通过改变 Employee 类型的实际子类,可以根据需要动态地切换雇员类型,并且可以将每个类型的特点和行为保持封装。

public abstract class Employee {
    public abstract int getTypeCode();
    public abstract int payAmount();
}

public class Engineer extends Employee {
    public int getTypeCode() {
        return 0;
    }

    public int payAmount() {
        // 计算工程师的薪水
        return 5000;
    }
}

public class Salesman extends Employee {
    public int getTypeCode() {
        return 1;
    }

    public int payAmount() {
        // 计算销售人员的薪水
        return 3000;
    }
}

public class Manager extends Employee {
    public int getTypeCode() {
        return 2;
    }

    public int payAmount() {
        // 计算经理的薪水
        return 8000;
    }
}

接下来,在程序中使用具体的子类来表示雇员类型。创建一个 Employee 类的对象时,可以根据不同的类型传入相应的子类,从而动态地切换雇员类型。也可以通过调用 getTypeCode 方法获取当前雇员的类型码,并通过调用 payAmount 方法计算出当前雇员的薪水。

Employee employee = new Engineer();
int typeCode = employee.getTypeCode();
int pay = employee.payAmount();

通过使用 Replace Type Code with Subclasses 重构,成功将原来的 typeCode 属性替换为具体的子类,提高了代码的可读性和可维护性,同时也增强了对各种类型的封装性和灵活性。需要注意的是,在使用该重构方法时,应仔细考虑每个子类的特点和意义,以保证新类的准确性和完整性,并适当地处理向后兼容性。

Replace Type Code with State/Strategy(以状态/策略取代类型码)

它的主要目的是通过引入状态或策略来替代类型码,从而提高代码的可读性、可维护性和灵活性。

举个例子,假设有一个名为 Employee 的类用于表示雇员信息,其中包含了一个公共属性 typeCode,表示雇员类型的代码。为了提高代码的可读性和可维护性,可以考虑使用 Replace Type Code with State/Strategy 重构,将 typeCode 属性替换为具体的状态或策略类。例如,可以使用状态模式来表示雇员类型的状态,或者使用策略模式来定义不同类型雇员的计算薪水的策略。

首先,可以创建一个抽象状态/策略类来表示不同的雇员类型,并在其中定义各自特有的属性和行为方法。然后,在 Employee 类中移除 typeCode 属性,并将其替换为相应的状态或策略类。这样可以根据需要动态地切换雇员类型,并且可以将每个类型的特点和行为保持封装。

状态模式:

public abstract class EmployeeType {
    public abstract int getTypeCode();
    public abstract int payAmount();
}

public class Engineer extends EmployeeType {
    public int getTypeCode() {
        return 0;
    }

    public int payAmount() {
        // 计算工程师的薪水
        return 5000;
    }
}

public class Salesman extends EmployeeType {
    public int getTypeCode() {
        return 1;
    }

    public int payAmount() {
        // 计算销售人员的薪水
        return 3000;
    }
}

public class Manager extends EmployeeType {
    public int getTypeCode() {
        return 2;
    }

    public int payAmount() {
        // 计算经理的薪水
        return 8000;
    }
}

public class Employee {
    private int monthlySalary;
    private int commission;
    private int bonus;
    private EmployeeType type;

    public Employee(int monthlySalary, int commission, int bonus, EmployeeType type) {
        this.monthlySalary = monthlySalary;
        this.commission = commission;
        this.bonus = bonus;
        this.type = type;
    }

    public int getTypeCode() {
        return type.getTypeCode();
    }

    public int payAmount() {
        return type.payAmount();
    }
}

策略模式:

public interface PayStrategy {
    int pay(Employee employee);
}

public class EngineerPayStrategy implements PayStrategy {
    public int pay(Employee employee) {
        // 计算工程师的薪水
        return 5000;
    }
}

public class SalesmanPayStrategy implements PayStrategy {
    public int pay(Employee employee) {
        // 计算销售人员的薪水
        return 3000;
    }
}

public class ManagerPayStrategy implements PayStrategy {
    public int pay(Employee employee) {
        // 计算经理的薪水
        return 8000;
    }
}

public class Employee {
    private int monthlySalary;
    private int commission;
    private int bonus;
    private PayStrategy payStrategy;

    public Employee(int monthlySalary, int commission, int bonus, PayStrategy payStrategy) {
        this.monthlySalary = monthlySalary;
        this.commission = commission;
        this.bonus = bonus;
        this.payStrategy = payStrategy;
    }

    public int payAmount() {
        return payStrategy.pay(this);
    }
}

接下来,在程序中使用具体的状态或策略类来表示不同的雇员类型,可以根据需要动态地切换雇员类型,并且可以将每个类型的特点和行为保持封装。

状态模式:

Employee employee = new Employee(1000, 500, 100, new Engineer());
int typeCode = employee.getTypeCode();
int pay = employee.payAmount();

策略模式:

Employee employee = new Employee(1000, 500, 100, new EngineerPayStrategy());
int pay = employee.payAmount();

通过使用 Replace Type Code with State/Strategy 重构,成功将原来的 typeCode 属性替换为相应的状态或策略类,提高了代码的可读性和可维护性,同时也增强了对各种类型的封装性和灵活性。需要注意的是,在使用该重构方法时,应仔细考虑每个状态或策略的特点和意义,以确保新类的准确性和完整性,并适当地处理向后兼容性。

Replace Subclass with Fields (以字段取代子类)

它的主要目的是通过使用字段来替代继承关系,从而简化代码结构,减少重复代码,提高可维护性和灵活性。

举个例子,假设有一个汽车制造公司,其中包含两种类型的汽车:轿车和卡车。这两种汽车都有不同的排量和最大速度属性。最初的实现中,将轿车和卡车分别定义为两个子类,如下所示:

public class Car {
    private int displacement;
    private int maxSpeed;

    public int getDisplacement() {
        return displacement;
    }

    public void setDisplacement(int displacement) {
        this.displacement = displacement;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
}

public class Sedan extends Car {
    public Sedan() {
        setDisplacement(1000);
        setMaxSpeed(200);
    }
}

public class Truck extends Car {
    public Truck() {
        setDisplacement(3000);
        setMaxSpeed(100);
    }
}

但是,这种实现方式存在一些问题。首先,子类的数量可能会非常多,如果每种类型的汽车都定义为一个子类,就会导致代码量过大。其次,如果需要新增一种汽车类型,就必须添加一个新的子类,这样会让代码变得越来越复杂。

为了解决这些问题,可以使用 “Replace Subclass with Fields” 重构方法。具体实现方式是将轿车和卡车的特定属性定义为一个枚举类型,然后在 Car 类中新增一个 type 字段,用于表示汽车类型。最后,根据不同的类型,使用相应的值来初始化 Car 实例中的各个字段。例如:

public enum CarType {
    SEDAN(1000, 200),
    TRUCK(3000, 100);

    private final int displacement;
    private final int maxSpeed;

    CarType(int displacement, int maxSpeed) {
        this.displacement = displacement;
        this.maxSpeed = maxSpeed;
    }

    public int getDisplacement() {
        return displacement;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }
}

public class Car {
    private int displacement;
    private int maxSpeed;
    private CarType type;

    public Car(CarType type) {
        this.type = type;
        this.displacement = type.getDisplacement();
        this.maxSpeed = type.getMaxSpeed();
    }

    public int getDisplacement() {
        return displacement;
    }

    public void setDisplacement(int displacement) {
        this.displacement = displacement;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
}

在上面的代码中,首先定义了一个 CarType 枚举类型,其中包含了每种汽车类型的特定属性。然后,在 Car 类中新增了一个 type 字段,用于表示汽车类型。最后,根据不同的类型,使用相应的值来初始化 Car 实例中的各个字段。这样,当需要新增一种汽车类型时,只需要在枚举类型中添加一个新值即可。

使用"Replace Subclass with Fields" 重构方法,可以将轿车和卡车的特定属性定义为一个枚举类型,用统一的方式进行管理,从而简化代码结构,减少重复代码,并提高代码的可维护性和灵活性。