简介
“Change Reference to Value”(将引用更改为值)是一种重构手法,当对象的标识(通过引用)比其实际状态更重要时,这种重构就很有用。通过将对象的引用替换为对象的值,可以简化对象之间的关系,提高代码的可维护性和可读性。以下是进行“Change Reference to Value”重构的详细步骤。
针对的症状(代码坏味道)
- 对象引用复杂:对象之间的引用关系复杂,导致难以理解和维护代码。
- 标识比状态更重要:对象的标识(通过引用)在代码逻辑中起关键作用,而不是对象的实际状态。
“Change Reference to Value”的详细步骤
- 确定要更改的对象
- 分析引用关系:在代码中找到引用关系复杂的对象,并且确定其标识是否比状态更重要。
- 评估对象状态:确保该对象的状态相对稳定,不会频繁变化导致值的不一致。
- 创建值对象
- 复制状态:创建一个新的类,该类将作为值对象,复制原对象中需要的值属性到新类。
- 提供必要方法:为新的值对象提供必要的访问方法(getter)和修改方法(setter),如果需要的话。
- 替换引用
- 更新引用处:在原代码中,找到所有引用原对象的地方,将其替换为使用新创建的值对象。
- 修改逻辑:由于值对象是不可变的(通常情况下),可能需要修改依赖原对象可变状态的逻辑。
- 测试
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 手动测试:如果有必要,进行手动测试以验证功能的正确性。
- 代码审查
- 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
- 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明将引用更改为值的影响。
示例
假设我们有一个Customer类,当前使用引用方式处理客户信息,我们希望将其更改为值对象方式。
原始代码
class Address {
private String street;
private String city;
public Address(String street, String city) {
this.street = street;
this.city = city;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
}
class Customer {
private Address address;
public Customer(Address address) {
this.address = address;
}
public Address getAddress() {
return address;
}
}
重构步骤
-
确定要更改的对象:
Customer类中的Address对象引用关系较为简单,且Address的状态相对稳定,适合进行“Change Reference to Value”重构。 -
创建值对象:
- 创建新的
AddressValue类:class AddressValue { private String street; private String city; public AddressValue(String street, String city) { this.street = street; this.city = city; } public String getStreet() { return street; } public String getCity() { return city; } }
- 创建新的
-
替换引用:修改
Customer类,使用AddressValue代替Address:class Customer { private AddressValue address; public Customer(AddressValue address) { this.address = address; } public AddressValue getAddress() { return address; } } -
测试:
- 编译代码,确保没有语法错误。
- 运行相关单元测试,验证功能是否正常。
-
代码审查:
- 邀请同事审查代码,确保重构没有引入新问题。
- 更新文档,说明
Address已从引用对象改为值对象。
练习
基础练习题
-
简单对象引用更改
-
给定以下 Java 代码,
Employee类中使用了Department类的引用。请将Department类的引用更改为值对象。class Department { private String name; public Department(String name) { this.name = name; } public String getName() { return name; } } class Employee { private Department department; public Employee(Department department) { this.department = department; } public Department getDepartment() { return department; } }
-
-
多层引用更改
-
下面的 Java 代码中有
Order类引用Product类,Product类又引用Category类。请将Category类的引用在Product类中改为值对象,并相应调整Order类。class Category { private String name; public Category(String name) { this.name = name; } public String getName() { return name; } } class Product { private String name; private Category category; public Product(String name, Category category) { this.name = name; this.category = category; } public String getName() { return name; } public Category getCategory() { return category; } } class Order { private Product product; public Order(Product product) { this.product = product; } public Product getProduct() { return product; } }
-
进阶练习题
-
引用更改与逻辑调整
-
在这段 Java 代码中,
Project类引用Team类,并且在Project类的assignTeam方法中有基于Team引用的逻辑。请将Team类的引用改为值对象,并调整assignTeam方法的逻辑。class Team { private String name; private int memberCount; public Team(String name, int memberCount) { this.name = name; this.memberCount = memberCount; } public String getName() { return name; } public int getMemberCount() { return memberCount; } } class Project { private Team team; public Project() { this.team = null; } public void assignTeam(Team team) { if (this.team == null) { this.team = team; } else { System.out.println("Team already assigned."); } } public Team getTeam() { return team; } }
-
-
值对象的不变性处理
-
给定以下 Java 代码,
User类引用Role类。请将Role类的引用改为值对象,并确保值对象的不变性,同时处理User类中与Role相关的逻辑。class Role { private String name; private boolean isAdmin; public Role(String name, boolean isAdmin) { this.name = name; this.isAdmin = isAdmin; } public String getName() { return name; } public boolean isAdmin() { return isAdmin; } public void setAdmin(boolean isAdmin) { this.isAdmin = isAdmin; } } class User { private Role role; public User(Role role) { this.role = role; } public Role getRole() { return role; } public boolean hasAdminAccess() { return role.isAdmin(); } }
-
综合拓展练习题
- 多类引用重构与代码审查模拟
-
考虑一个简单的 Java 图书馆系统,有
Book类、Author类、Publisher类。Book类引用Author类和Publisher类,Author类又引用Publisher类。同时,Book类中的printBookInfo方法和Author类中的printAuthorInfo方法都有基于这些引用的复杂逻辑。 -
请对这些类进行“Change Reference to Value”重构,将合适的引用改为值对象,并调整相关方法的逻辑。
-
假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
class Publisher { private String name; private String address; public Publisher(String name, String address) { this.name = name; this.address = address; } public String getName() { return name; } public String getAddress() { return address; } } class Author { private String name; private Publisher publisher; public Author(String name, Publisher publisher) { this.name = name; this.publisher = publisher; } public String getName() { return name; } public Publisher getPublisher() { return publisher; } public void printAuthorInfo() { System.out.println("Author: " + name + ", Publisher: " + publisher.getName()); } } class Book { private String title; private Author author; private Publisher publisher; public Book(String title, Author author, Publisher publisher) { this.title = title; this.author = author; this.publisher = publisher; } public String getTitle() { return title; } public Author getAuthor() { return author; } public Publisher getPublisher() { return publisher; } public void printBookInfo() { System.out.println("Book: " + title + ", Author: " + author.getName() + ", Publisher: " + publisher.getName()); } }
-