嗅觉坏代码-散弹式修改(Shotgun Surgery)
散弹式修改(Shotgun Surgery)是指在软件开发中,当需要修改一个功能时,需要同时修改多个类或方法,导致代码的可维护性下降。这种情况下,修改代码就像是用散弹枪射击,一发子弹可以命中多个目标,但也增加了代码的复杂性和风险。
散弹式修改的问题通常出现在代码设计不合理或耦合度过高的情况下。当多个类之间存在紧密的依赖关系,修改其中一个类的属性或方法时,就需要同时修改其他相关的类,以确保系统的一致性。这种修改方式不仅繁琐,还容易引入错误和不一致性。
为了解决散弹式修改的问题,我们可以使用重构技术中的封装(Encapsulation)手法。封装的目标是将相关的属性和方法封装在一起,形成一个独立的单元,从而减少对外部的依赖,提高代码的可维护性和灵活性。
封装的步骤如下:
- 识别需要修改的功能,并确定需要修改的类和方法。
- 创建一个新的类,用于封装相关的属性和方法。
- 将原始的类中的属性和方法复制到新的类中,并将它们设置为私有的。
- 在新的类中提供公共的访问方法(getter 和 setter)来访问和修改属性。
- 修改其他相关的类,将其方法的参数修改为接收新的类对象作为参数,而不是单独的属性。
- 在相关的类中,通过调用新的类对象的公共方法来执行相应的操作。
通过封装的方式,我们将相关的属性和方法封装在一个统一的类中,减少了对外部的依赖。这样,当需要修改功能时,只需要修改封装类的代码,而不会影响到其他类。这提高了代码的可维护性,减少了散弹式修改的问题。
除了封装手法,还有其他一些重构技术可以用来解决散弹式修改的问题,例如提取方法、提取类、应用设计模式等。选择合适的重构技术取决于具体的情况和需求。
总结起来,散弹式修改是一个常见的代码维护问题,会导致代码的可维护性下降。通过使用封装等重构技术,我们可以将相关的属性和方法封装在一起,减少对外部的依赖,提高代码的可维护性和灵活性。这样,当需要修改功能时,只需要修改封装类的代码,而不需要修改多个类,从而降低了代码的复杂性和风险。
代码
假设我们有一个简单的学生管理系统,其中包含以下几个Java类:Student、Course和Teacher。每个类都有一些属性和方法,用于表示学生、课程和教师的相关信息和操作。
现在,假设我们需要修改系统中的一个功能:当学生选择一门课程时,需要更新学生的选课列表,并将学生添加到课程的学生列表中。在当前的设计中,这个功能涉及到多个类的修改,属于散弹式修改的情况。
为了解决这个问题,我们可以使用封装手法来重构代码。首先,我们创建一个新的类Enrollment,用于封装学生的选课信息。Enrollment类包含以下属性和方法:
public class Enrollment {
private Student student;
private Course course;
public Enrollment(Student student, Course course) {
this.student = student;
this.course = course;
}
// Getter and setter methods for student and course
// Other methods related to enrollment
}
然后,我们对Student类进行修改,将选课相关的属性和方法封装在Enrollment类中:
public class Student {
private String name;
private List<Enrollment> enrollments;
public Student(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
public void enroll(Course course) {
Enrollment enrollment = new Enrollment(this, course);
enrollments.add(enrollment);
course.addStudent(this);
}
// Other methods related to student
}
接下来,我们对Course类进行修改,将选课相关的属性和方法封装在Enrollment类中:
public class Course {
private String name;
private List<Enrollment> enrollments;
public Course(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
public void addStudent(Student student) {
Enrollment enrollment = new Enrollment(student, this);
enrollments.add(enrollment);
student.addEnrollment(enrollment);
}
// Other methods related to course
}
通过封装的方式,我们将选课相关的属性和方法封装在Enrollment类中,减少了对外部的依赖。现在,当学生选择一门课程时,只需要调用Student类的enroll方法,而不需要直接修改Course类。这样,我们解决了散弹式修改的问题,提高了代码的可维护性和灵活性。
封装手法在Java中是一种常用的重构技术,可以帮助我们解决散弹式修改的问题,并提高代码的可读性和可测试性。通过将相关的代码组织在一起,我们可以更清晰地理解其功能和作用,也更容易编写和执行相应的测试。