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

2 阅读4分钟

简介

“封装集合”(Encapsulate Collection)是一种重构手法,其核心目的是对类中的集合属性进行封装,以增强代码的安全性和可维护性。当直接暴露集合属性时,外部代码可以随意修改集合内容,这可能会破坏类的内部状态和业务逻辑。通过封装集合,可以控制对集合的访问和修改方式,只提供必要的操作接口。

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

  • 集合属性直接暴露:类中的集合属性被声明为公共的,外部代码可以直接访问和修改该集合。
  • 无法控制集合修改:外部代码可以自由地向集合中添加、删除元素,可能导致类的内部状态不一致。
  • 代码耦合度高:外部代码依赖于集合的具体实现,当集合的实现发生变化时,需要修改大量的外部代码。

封装集合(Encapsulate Collection)的详细步骤

  1. 检查集合属性
    • 找出类中直接暴露的集合属性,确认这些集合属性被外部代码直接访问和修改的情况。
  2. 将集合属性设为私有
    • 把集合属性的访问修饰符改为 private,防止外部代码直接访问该集合。
  3. 提供访问方法
    • 提供只读访问方法:创建一个方法来返回集合的副本,而不是直接返回集合本身,这样可以避免外部代码修改集合。
    • 提供修改方法:根据业务需求,提供必要的方法来添加、删除集合中的元素,确保对集合的修改符合类的业务逻辑。
  4. 测试
    • 编译代码:确保代码编译通过,没有任何语法错误。
    • 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
    • 手动测试:如果有必要,进行手动测试以验证功能的正确性。
  5. 代码审查
    • 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
    • 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明封装集合的影响。

示例

假设有一个 Student 类,其中包含一个 List 类型的 courses 集合,该集合直接暴露给外部。

原始代码

import java.util.ArrayList;
import java.util.List;

public class Student {
    public List<String> courses = new ArrayList<>();
}

重构步骤

  1. 检查集合属性:courses 集合被声明为 public,外部代码可以直接访问和修改。
  2. 将集合属性设为私有:把 courses 的访问修饰符改为 private
  3. 提供访问方法:
    • 提供只读访问方法:创建 getCourses 方法,返回 courses 的副本。
    • 提供修改方法:创建 addCourseremoveCourse 方法来控制对集合的修改。

重构后代码

import java.util.ArrayList;
import java.util.List;

public class Student {
    private List<String> courses = new ArrayList<>();

    public List<String> getCourses() {
        return new ArrayList<>(courses);
    }

    public void addCourse(String course) {
        courses.add(course);
    }

    public void removeCourse(String course) {
        courses.remove(course);
    }
}

练习

基础练习题

  1. 简单集合封装

    • 给定以下 Java 代码,Library 类中的 books 集合直接暴露。请封装该集合。

        import java.util.ArrayList;
      import java.util.List;
      
      public class Library {
          public List<String> books = new ArrayList<>();
      }
      
  2. 多类型集合封装

    • 下面的 Java 代码中,Inventory 类包含一个 Map 类型的 products 集合,该集合直接暴露。请封装该集合。
          import java.util.HashMap;
    import java.util.Map;
    
    public class Inventory {
        public Map<String, Integer> products = new HashMap<>();
    }
    

进阶练习题

  1. 集合封装与业务逻辑控制

    • 在这段 Java 代码中,Team 类的 players 集合直接暴露。要求封装该集合,并在添加球员时,确保球队人数不超过 11 人。

        import java.util.ArrayList;
      import java.util.List;
      
      public class Team {
          public List<String> players = new ArrayList<>();
      }
      
  2. 集合封装与返回值处理

    • 给定以下 Java 代码,Course 类的 students 集合直接暴露。请封装该集合,并在获取学生列表时,返回按字母顺序排序后的副本。
    import java.util.ArrayList;
    import java.util.List;
    
    public class Course {
        public List<String> students = new ArrayList<>();
    }
    

综合拓展练习题

  1. 多集合封装与代码审查模拟
    • 考虑一个简单的 Java 电商系统,有 Order 类,其中包含 products 集合(List<Product>)和 discounts 集合(Map<String, Double>),这两个集合都直接暴露。请对这些集合进行 “封装集合” 重构。

    • 假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。

      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      class Product {
         private String name;
         private double price;
      
         public Product(String name, double price) {
            this.name = name;
            this.price = price;
         }
      
         public String getName() {
            return name;
         }
      
         public double getPrice() {
            return price;
         }
      }
      
      public class Order {
         public List<Product> products = new ArrayList<>();
         public Map<String, Double> discounts = new HashMap<>();
      }