Java代码重构学习笔记-在对象之间搬移特性
Move Method(搬移函数)
它的目的是将一个方法从原始类中搬移到其他类中,从而使类的职责更加清晰明确。
举个例子,假设有一个名为 Customer 的类,其中包含了一个用于计算客户折扣的方法 applyDiscount:
public class Customer {
private String name;
private int discount;
public void applyDiscount(double amount) {
amount *= (1 - discount / 100.0);
System.out.println(name + " applied a discount of " + discount + "%, the final amount is " + amount);
}
// getter and setter methods for name and discount
}
在这段代码中,applyDiscount 方法用于计算客户的折扣,并在控制台上输出折扣信息。然而,applyDiscount 方法并没有直接与 Customer 类的数据进行交互,因此可以考虑使用 Move Method 进行重构,将该方法搬移到一个新的 DiscountCalculator 类中,比如:
public class Customer {
private String name;
private int discount;
// getter and setter methods for name and discount
public String getName() {
return name;
}
public int getDiscount() {
return discount;
}
}
public class DiscountCalculator {
public void applyDiscount(Customer customer, double amount) {
int discount = customer.getDiscount();
amount *= (1 - discount / 100.0);
System.out.println(customer.getName() + " applied a discount of " + discount + "%, the final amount is " + amount);
}
}
通过 Move Method 重构后,applyDiscount 方法被搬移到了 DiscountCalculator 类中,从而使 Customer 类的职责更加明确,只负责处理与客户信息相关的数据和操作。DiscountCalculator 类则专门用于计算折扣,使得各自的职责更加清晰明确。
需要注意的是,在进行 Move Method 重构时,需要确保新类具有明确的职责,并且确保搬移方法的逻辑正确性和稳定性。同时还需要考虑搬移方法可能涉及到的依赖关系、访问权限和继承关系等,确保程序的整体结构和功能不会发生变化。
Move Field(搬移字段)
它的目的是将一个字段(属性)从一个类中搬移到另一个相关的类中,从而使类的职责更加清晰明确。
举个例子,假设有一个名为 Employee 的类,其中包含了员工的基本信息和员工部门信息,如下所示:
public class Employee {
private String name;
private int age;
private String department;
// setter and getter methods for name, age, department
}
在这段代码中,Employee 类包含了员工名字、年龄和所在部门等信息。然而,随着业务规模扩大,可能需要对员工信息和部门信息进行更加细粒度的管理,因此可以考虑使用 Move Field 进行重构,将 department 字段搬移到一个新的 Department 类中,如下所示:
public class Employee {
private String name;
private int age;
private Department department;
// getter and setter methods for name, age, department
}
public class Department {
private String name;
// getter and setter methods for name
}
通过 Move Field 重构后,Employee 类将员工部门信息委托给一个新的 Department 类来管理,从而使得两个类各司其职,相互独立。在后续对员工和部门信息进行操作时,可以更加方便地控制和管理。
需要注意的是,在进行 Move Field 重构时,要确保新类具有明确的职责,并且需要确保搬移字段的逻辑正确性和稳定性。同时,还需要考虑数据访问权限、继承关系以及可能涉及到的依赖关系等因素,确保程序的整体结构和功能不会发生变化,同时提高代码的可读性和可维护性。
Extract Class(提炼类)
它的目的是将一个类中的一部分职责提取出来,组成一个新的类。这样可以将职责分离,降低类的复杂度,提高代码的可读性和可维护性。
举个例子,假设有一个名为 Person 的类,其中包含了人的基本信息、地址信息和联系方式,如下所示:
public class Person {
private String name;
private int age;
private String street;
private String city;
private String zipCode;
private String phone;
private String email;
// getter and setter methods for name, age, street, city, zipCode, phone, email
}
在这段代码中,Person 类包含了人的各种信息,然而这些信息并没有很好地进行划分和组织。如果需要对地址信息进行更加详细的管理,可以考虑使用 Extract Class 进行重构,将地址信息以及相关操作提取为一个新的 Address 类。
public class Person {
private String name;
private int age;
private Address address;
private String phone;
private String email;
// getter and setter methods for name, age, address, phone, email
}
public class Address {
private String street;
private String city;
private String zipCode;
// getter and setter methods for street, city, zipCode
}
通过 Extract Class 重构后,Person 类职责更加明确,只负责处理人的基本信息和联系方式,Address 类则专门用于管理地址信息,使得代码更加清晰易懂。在后续修改和扩展时,可以分别对两个类进行操作,减少了代码重复和耦合。
需要注意的是,在进行 Extract Class 重构时,要确保新类具有明确的职责,并且要确保拆分出来的类能够独立完成相应的操作。同时,要考虑数据访问权限、继承关系以及可能涉及到的依赖关系等因素,确保程序的整体结构和功能不会发生变化,同时提高代码的可读性和可维护性。
Inline Class(将类内联化)
它的目的是消除过度抽象的中间层,将一个只有少量属性和方法的类直接合并到另一个类中,从而简化程序的结构。
举个例子,假设有一个名为 Customer 的类,其中只包含了一个名字属性和一个地址属性,如下所示:
public class Customer {
private String name;
private Address address;
// getter and setter methods for name, address
}
public class Address {
private String street;
private String city;
private String zipCode;
// getter and setter methods for street, city, zipCode
}
在这段代码中,Customer 类只包含了一个名字属性和一个地址属性,而 Address 类则包含了具体的地址信息。如果 Customer 类只在程序的某个非常狭窄的范围内使用,并且不涉及到地址信息的处理,可以考虑使用 Inline Class 进行重构,将 Customer 类中的属性和方法直接合并到另一个类中。
public class Order {
private String customerName;
private String street;
private String city;
private String zipCode;
// getter and setter methods for customerName, street, city, zipCode
}
通过 Inline Class 重构后,Customer 类被完全内联到 Order 类中,简化了程序的结构,同时也提高了代码的可读性和可维护性。在后续修改和扩展时,只需要对 Order 类进行操作即可。
需要注意的是,在进行 Inline Class 重构时,要确保被内联的类只有少量的属性和方法,并且没有其他用途。同时,也要考虑程序的继承关系、依赖关系等因素,确保程序的整体结构和功能不会发生变化,同时提高代码的可读性和可维护性。
Hide Delegate(隐藏“委托关系”)
它的目的是减少类之间的耦合程度,将一个类对另一个类的调用关系隐藏起来,从而简化程序的结构。
举个例子,假设有一个名为 Department 的类,其中包含了员工管理的相关信息,如下所示:
public class Department {
private String name;
private List<Employee> employees;
// getter and setter methods for name, employees
public Employee getManager() {
return employees.get(0);
}
}
在这段代码中,Department 类中包含了一个名字属性和一个员工列表属性,同时定义了一个 getManager() 方法,该方法返回员工列表中的第一个元素,即部门经理。这里存在一个明显的委托关系,即 Department 类委托给 Employee 类来处理部门经理的获取操作。
如果在程序中需要频繁地获取部门经理信息,并且考虑到后续可能会更改获取逻辑,也许应当采用 Hide Delegate 进行重构,将 Department 类中的委托关系隐藏起来,使得其他类不需要直接依赖于 Employee 类。
public class Department {
private String name;
private List<Employee> employees;
// getter and setter methods for name, employees
public Employee getManager() {
return new Manager(employees.get(0));
}
}
public class Manager {
private Employee employee;
public Manager(Employee employee) {
this.employee = employee;
}
public String getName() {
return employee.getName();
}
// other methods related to manager
}
通过 Hide Delegate 重构后,Department 类中的委托关系被隐藏到一个新的 Manager 类中,其他类可以通过 Department 类提供的接口间接获取部门经理信息,而不需要直接依赖于 Employee 类。在后续修改和扩展时,也能够更加方便地进行维护和扩展。
需要注意的是,在进行 Hide Delegate 重构时,要确保隐藏的委托关系能够被其他类所使用,并且不会影响代码的原有功能。同时也要考虑程序的依赖关系、继承关系和设计初衷等因素,确保程序的整体结构和功能不会发生变化,并提高代码的可读性和可维护性。
Remove Middle Man(移除中间人)
它的目的是消除过度抽象的中间层,简化程序的结构,减少不必要的委托关系。
举个例子,假设有一个名为 Person 的类,其中包含了姓名、联系方式等个人信息,同时定义了一个 getPhoneNumber() 方法来获取个人的电话号码,如下所示:
public class Person {
private String name;
private String phoneNumber;
private String email;
// getter and setter methods for name, phoneNumber, email
public String getPhoneNumber() {
return phoneNumber;
}
}
在这段代码中,Person 类包含了个人信息的属性和一个用于获取电话号码的方法。假设现在需要获取与该 person 关联的 company 的电话号码,可以通过在 Person 类中添加一个 getCompanyPhoneNumber() 方法实现:
public class Person {
private String name;
private String phoneNumber;
private String email;
private Company company;
// getter and setter methods for name, phoneNumber, email
public String getPhoneNumber() {
return phoneNumber;
}
public String getCompanyPhoneNumber() {
if (company != null) {
return company.getPhoneNumber(); // 委托给 Company 对象进行处理
} else {
return null;
}
}
}
public class Company {
private String name;
private String phoneNumber;
// getter and setter methods for name, phoneNumber
}
在这段代码中,Person 类中增加了一个 getCompanyPhoneNumber() 方法,该方法将获取公司电话号码的操作委托给了另一个 Company 类,通过中间的 company 对象来间接获取公司电话号码。
如果考虑到代码的可读性和可维护性,也许可以采用 Remove Middle Man 进行重构,去除不必要的中间层,直接在客户端调用 Company 类中的 getPhoneNumber() 方法。
public class Person {
private String name;
private String phoneNumber;
private String email;
private Company company;
// getter and setter methods for name, phoneNumber, email
public String getPhoneNumber() {
return phoneNumber;
}
public Company getCompany() {
return company;
}
}
public class Company {
private String name;
private String phoneNumber;
// getter and setter methods for name, phoneNumber
}
// client code
Person person = new Person();
Company company = person.getCompany();
String companyPhoneNumber = company != null ? company.getPhoneNumber() : null;
通过 Remove Middle Man 重构后,Person 类中不再包含 getCompanyPhoneNumber() 方法,而是直接返回 company 对象,在客户端可以直接调用 Company 类中的 getPhoneNumber() 方法,从而消除了过度的中间层,简化了程序的结构,提高了代码的可读性和可维护性。
需要注意的是,在进行 Remove Middle Man 重构时,要确保移除中间层不会影响原有的功能,并且能够提高代码的可读性和可维护性。同时也要考虑程序的依赖关系、继承关系和设计初衷等因素,确保程序的整体结构和功能不会发生变化。
Introduce Foreign Method(引入外加函数)
它的主要目的是在无法修改现有代码的情况下,向对象中添加一个新的方法(即所谓的“外来函数”),以完成特定的操作。
举个例子,假设有一个名为 DateUtil 的工具类,其中包含了一些可以方便地操作日期的静态方法,如下所示:
public class DateUtil {
public static Date getNextWeekday(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == Calendar.FRIDAY) {
calendar.add(Calendar.DATE, 3);
} else if (dayOfWeek == Calendar.SATURDAY) {
calendar.add(Calendar.DATE, 2);
} else {
calendar.add(Calendar.DATE, 1);
}
return calendar.getTime();
}
}
在这段代码中,DateUtil 类包含了一个 getNextWeekday() 方法,用于获取下一个工作日的日期。如果需要在另一个类中频繁地使用 getNextWeekday() 方法, 可以考虑使用 Introduce Foreign Method 重构,向该类中添加一个外来函数,使得其他类可以直接调用该函数,而不需要再次访问 DateUtil 类。
public class SomeClass {
private Date date;
// getter and setter methods for date
public Date getNextWeekday() {
return DateUtil.getNextWeekday(date);
}
}
通过 Introduce Foreign Method 重构后,SomeClass 类中添加了一个 getNextWeekday() 方法,该方法直接委托给 DateUtil 类中的 getNextWeekday() 方法进行处理,在客户端可以直接调用 SomeClass 类中的 getNextWeekday() 方法来获取下一个工作日的日期。
需要注意的是,使用 Introduce Foreign Method 重构时要确保该外来函数能够被其他类所使用,并且不会影响原有代码的行为和功能。同时也要考虑程序的依赖关系、继承关系和设计初衷等因素,确保程序的整体结构和功能不会发生变化。
Introduce Local Extension (引入本地扩展)
它的主要目的是在无法修改现有代码的情况下,向现有类中添加新的方法和属性,使其更加灵活和易用。
举个例子,假设有一个名为 Employee 的类,用于表示一个员工对象,该类包含了员工的姓名、工号、月薪等信息。现在需要为该类添加一个新的功能,即计算员工的年终奖金,假设计算公式为月薪的 1.5 倍。但由于不允许修改 Employee 类的源代码,因此不能直接在该类中添加一个计算年终奖金的方法。
这时可以考虑使用 Introduce Local Extension 重构,定义一个子类或者放置在同一个包下的新类,添加一个计算年终奖金的方法,将 Employee 对象作为该方法的参数进行计算。
public class Employee {
private String name;
private String id;
private double monthlySalary;
// getter and setter methods for name, id, monthlySalary
}
public class EmployeeExtension {
private Employee employee;
public EmployeeExtension(Employee employee) {
this.employee = employee;
}
public double getYearEndBonus() {
return employee.getMonthlySalary() * 1.5;
}
}
在上述代码中,定义了一个名为 EmployeeExtension 的类,该类包含了一个指向 Employee 对象的引用,同时添加了一个计算年终奖金的方法 getYearEndBonus()。客户端可以通过创建 EmployeeExtension 对象,将 Employee 对象作为参数传入该对象进行操作。
Employee employee = new Employee();
// set employee's name, id, monthlySalary
EmployeeExtension extension = new EmployeeExtension(employee);
double bonus = extension.getYearEndBonus();
通过 Introduce Local Extension 重构后,成功添加了计算年终奖金的功能,同时也保留了原有 Employee 类的功能和行为。需要注意的是,在使用 Introduce Local Extension 重构时,要确保添加的新方法和属性能够满足需求,同时也要考虑程序的依赖关系和设计初衷等因素,确保程序的整体结构和功能不会发生变化。