重构手法——数据重组类 | 数据完整性操作 | 封装字段

65 阅读5分钟

简介

“封装字段 - 自我封装字段”(Encapsulate Field - Self - Encapsulate Field)是一种重构手法,旨在增强代码的封装性、可维护性和灵活性。直接访问类的字段会破坏类的封装性,使得类的内部状态容易被外部随意修改,增加了代码的耦合度。通过自我封装字段,我们将字段的访问和修改操作封装在类内部的方法中,这样可以更好地控制字段的使用,方便后续对字段的访问逻辑进行修改和扩展。

针对的症状(代码坏味道)

  • 字段直接暴露:类中的字段被声明为公共(public)或受保护(protected),外部代码可以直接访问和修改这些字段,破坏了类的封装性。
  • 缺乏访问控制:没有对字段的访问和修改进行任何限制,可能导致数据不一致或违反业务规则。
  • 代码耦合度高:外部代码依赖于字段的具体实现,当字段的类型或访问逻辑发生变化时,需要修改大量的外部代码。

封装字段 - 自我封装字段(Encapsulate Field - Self - Encapsulate Field)的详细步骤

  1. 将字段设为私有
    • 把需要封装的字段的访问修饰符改为 private,防止外部代码直接访问该字段。
  2. 创建访问方法
    • 创建 getter 方法:为字段创建一个公共的 getter 方法,用于获取字段的值。方法名通常遵循 getFieldName 的命名规则(如果是布尔类型的字段,通常使用 isFieldName)。
    • 创建 setter 方法(可选):如果需要允许外部代码修改变量的值,为字段创建一个公共的 setter 方法。在 setter 方法中,可以添加数据验证逻辑,确保赋给字段的值是合法的。
  3. 在类内部使用访问方法
    • 将类内部所有直接访问该字段的代码替换为调用相应的 gettersetter 方法。这样做的好处是,当需要修改字段的访问逻辑时,只需要在 gettersetter 方法中进行修改,而不需要在类的多个地方进行修改。
  4. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证功能的正确性。
  5. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明封装字段的影响。

示例

假设有一个 Rectangle 类,其中的 widthheight 字段直接暴露。

原始代码

public class Rectangle {
   public double width;
   public double height;

   public double getArea() {
      return width * height;
   }
}

重构步骤

  1. 将字段设为私有:把 widthheight 字段的访问修饰符改为 private
  2. 创建访问方法:为 widthheight 字段分别创建 gettersetter 方法。
  3. 在类内部使用访问方法:将 getArea 方法中直接访问 widthheight 字段的代码替换为调用 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();
   }
}

练习

基础练习题

  1. 简单字段封装

    • 给定以下 Java 代码,Circle 类中的 radius 字段直接暴露。请对该字段进行封装,并在类内部使用访问方法。

        public class Circle {
         public double radius;
      
         public double getCircumference() {
            return 2 * Math.PI * radius;
         }
      }
      
  2. 布尔类型字段封装

    • 下面的 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.");
          }
       }
    }
    

进阶练习题

  1. 字段封装与复杂验证逻辑

    • 在这段 Java 代码中,Employee 类的 salary 字段直接暴露。要求对该字段进行封装,在 setter 方法中添加验证逻辑,确保工资不低于当地最低工资标准(假设最低工资标准为 2000),并在类内部使用访问方法。

      public class Employee {
         public double salary;
      
         public void giveRaise(double raiseAmount) {
            salary += raiseAmount;
         }
      }
      
  2. 字段封装与业务逻辑关联

    • 给定以下 Java 代码,BankAccount 类的 balance 字段直接暴露。请对该字段进行封装,在取款操作(withdraw 方法)时,确保余额足够,并在类内部使用访问方法。
public class BankAccount {
   public double balance;

   public void withdraw(double amount) {
      balance -= amount;
   }
}

综合拓展练习题

  1. 多字段封装与代码审查模拟

    • 考虑一个简单的 Java 学生管理系统,有 Student 类,其中包含 nameagegrade 字段,这些字段都直接暴露。请对这些字段进行 “封装字段 - 自我封装字段” 重构。
    • 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
    public class Student {
       public String name;
       public int age;
       public double grade;
    
       public String getStudentInfo() {
          return "Name: " + name + ", Age: " + age + ", Grade: " + grade;
       }
    }