简介
“封装字段 - 自我封装字段”(Encapsulate Field - Self - Encapsulate Field)是一种重构手法,旨在增强代码的封装性、可维护性和灵活性。直接访问类的字段会破坏类的封装性,使得类的内部状态容易被外部随意修改,增加了代码的耦合度。通过自我封装字段,我们将字段的访问和修改操作封装在类内部的方法中,这样可以更好地控制字段的使用,方便后续对字段的访问逻辑进行修改和扩展。
针对的症状(代码坏味道)
- 字段直接暴露:类中的字段被声明为公共(
public)或受保护(protected),外部代码可以直接访问和修改这些字段,破坏了类的封装性。 - 缺乏访问控制:没有对字段的访问和修改进行任何限制,可能导致数据不一致或违反业务规则。
- 代码耦合度高:外部代码依赖于字段的具体实现,当字段的类型或访问逻辑发生变化时,需要修改大量的外部代码。
封装字段 - 自我封装字段(Encapsulate Field - Self - Encapsulate Field)的详细步骤
- 将字段设为私有
- 把需要封装的字段的访问修饰符改为
private,防止外部代码直接访问该字段。
- 把需要封装的字段的访问修饰符改为
- 创建访问方法
- 创建
getter方法:为字段创建一个公共的getter方法,用于获取字段的值。方法名通常遵循getFieldName的命名规则(如果是布尔类型的字段,通常使用isFieldName)。 - 创建
setter方法(可选):如果需要允许外部代码修改变量的值,为字段创建一个公共的setter方法。在setter方法中,可以添加数据验证逻辑,确保赋给字段的值是合法的。
- 创建
- 在类内部使用访问方法
- 将类内部所有直接访问该字段的代码替换为调用相应的
getter和setter方法。这样做的好处是,当需要修改字段的访问逻辑时,只需要在getter和setter方法中进行修改,而不需要在类的多个地方进行修改。
- 将类内部所有直接访问该字段的代码替换为调用相应的
- 测试
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 手动测试:如果有必要,进行手动测试以验证功能的正确性。
- 代码审查
- 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
- 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明封装字段的影响。
示例
假设有一个 Rectangle 类,其中的 width 和 height 字段直接暴露。
原始代码
public class Rectangle {
public double width;
public double height;
public double getArea() {
return width * height;
}
}
重构步骤
- 将字段设为私有:把
width和height字段的访问修饰符改为private。 - 创建访问方法:为
width和height字段分别创建getter和setter方法。 - 在类内部使用访问方法:将
getArea方法中直接访问width和height字段的代码替换为调用getter方法。
重构后代码
public class Rectangle {
private double width;
private double height;
public double getWidth() {
return width;
}
public void setWidth(double width) {
if (width > 0) {
this.width = width;
} else {
throw new IllegalArgumentException("Width must be positive.");
}
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
if (height > 0) {
this.height = height;
} else {
throw new IllegalArgumentException("Height must be positive.");
}
}
public double getArea() {
return getWidth() * getHeight();
}
}
练习
基础练习题
-
简单字段封装
-
给定以下 Java 代码,
Circle类中的radius字段直接暴露。请对该字段进行封装,并在类内部使用访问方法。public class Circle { public double radius; public double getCircumference() { return 2 * Math.PI * radius; } }
-
-
布尔类型字段封装
- 下面的 Java 代码中,
User类的isLoggedIn字段直接暴露。请对该字段进行封装,并在类内部使用访问方法。
public class User { public boolean isLoggedIn; public void performAction() { if (isLoggedIn) { System.out.println("Performing action..."); } else { System.out.println("Please log in first."); } } } - 下面的 Java 代码中,
进阶练习题
-
字段封装与复杂验证逻辑
-
在这段 Java 代码中,
Employee类的salary字段直接暴露。要求对该字段进行封装,在setter方法中添加验证逻辑,确保工资不低于当地最低工资标准(假设最低工资标准为 2000),并在类内部使用访问方法。public class Employee { public double salary; public void giveRaise(double raiseAmount) { salary += raiseAmount; } }
-
-
字段封装与业务逻辑关联
- 给定以下 Java 代码,
BankAccount类的balance字段直接暴露。请对该字段进行封装,在取款操作(withdraw方法)时,确保余额足够,并在类内部使用访问方法。
- 给定以下 Java 代码,
public class BankAccount {
public double balance;
public void withdraw(double amount) {
balance -= amount;
}
}
综合拓展练习题
-
多字段封装与代码审查模拟
- 考虑一个简单的 Java 学生管理系统,有
Student类,其中包含name、age和grade字段,这些字段都直接暴露。请对这些字段进行 “封装字段 - 自我封装字段” 重构。 - 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
public class Student { public String name; public int age; public double grade; public String getStudentInfo() { return "Name: " + name + ", Age: " + age + ", Grade: " + grade; } } - 考虑一个简单的 Java 学生管理系统,有