简介
“提取超类”(Extract Superclass)是一种重要的重构手法,用于处理多个类存在相似结构和行为的情况。当多个类有共同的属性、方法或行为时,将这些共性提取到一个新的超类中,可以减少代码重复,提高代码的可维护性和可扩展性。通过继承超类,子类可以复用超类的代码,并且可以根据自身需求进行扩展。
针对的症状(代码坏味道)
- 代码重复:多个类中存在大量相同或相似的属性和方法,违反了代码复用原则。
- 难以修改:当需要修改这些重复的代码时,需要在多个类中进行修改,容易出现遗漏,导致代码不一致。
- 缺乏扩展性:如果需要添加新的共性功能,需要在每个类中重复添加,增加了开发和维护的成本。
提取超类(Extract Superclass)的详细步骤
- 识别共性
- 分析多个类的属性和方法,找出它们之间的共同部分,包括字段、方法签名和方法实现。
- 创建超类
- 根据识别出的共性,创建一个新的超类。超类的名称应能够清晰地描述其代表的抽象概念。
- 移动共性成员
- 将共同的属性和方法从子类移动到超类中。如果方法的实现完全相同,可以直接移动;如果实现部分不同,可以在超类中定义抽象方法,由子类进行具体实现。
- 让子类继承超类
- 修改子类的定义,让它们继承自新创建的超类。
- 调整子类代码
- 移除子类中因移动到超类而变得多余的代码。如果子类需要对超类的方法进行扩展或修改,可以通过重写方法来实现。
- 测试
- 编译代码:确保代码编译通过,没有任何语法错误。
- 运行测试:运行所有相关的单元测试,确保重构操作没有引入新的错误。
- 手动测试:如果有必要,进行手动测试以验证功能的正确性。
- 代码审查
- 同行评审:让同事或其他团队成员审查你的更改,确保代码质量和可维护性没有下降。
- 文档更新:如果项目有维护文档的习惯,记得更新相关文档,说明提取超类的影响。
示例
假设有 Dog 和 Cat 两个类,它们都有 name 属性和 makeSound 方法,存在代码重复。
原始代码
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void makeSound() {
System.out.println(name + " says Woof!");
}
}
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void makeSound() {
System.out.println(name + " says Meow!");
}
}
重构步骤
- 识别共性:
Dog和Cat类都有name属性和getName方法,makeSound方法虽然实现不同,但方法签名相同。 - 创建超类:创建
Animal超类。 - 移动共性成员:将
name属性和getName方法移动到Animal超类中,makeSound方法定义为抽象方法。 - 让子类继承超类:让
Dog和Cat类继承自Animal超类。 - 调整子类代码:移除子类中重复的
name属性和getName方法。
重构后代码
// 超类 Animal
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void makeSound();
}
// 子类 Dog
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(getName() + " says Woof!");
}
}
// 子类 Cat
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(getName() + " says Meow!");
}
}
练习
基础练习题
- 简单类的超类提取
-
给定以下 Java 代码,
Rectangle和Square类都有width和height属性(Square的width和height相等),以及计算面积的方法。请提取一个超类来减少代码重复。class Rectangle { protected int width; protected int height; public Rectangle(int width, int height) { this.width = width; this.height = height; } public int getArea() { return width * height; } } class Square { protected int side; public Square(int side) { this.side = side; } public int getArea() { return side * side; } }
-
- 多方法类的超类提取
-
下面的 Java 代码中,
Car和Truck类都有startEngine、stopEngine和move方法。请提取一个超类来重构代码。class Car { public void startEngine() { System.out.println("Car engine started."); } public void stopEngine() { System.out.println("Car engine stopped."); } public void move() { System.out.println("Car is moving."); } } class Truck { public void startEngine() { System.out.println("Truck engine started."); } public void stopEngine() { System.out.println("Truck engine stopped."); } public void move() { System.out.println("Truck is moving."); } }
-
进阶练习题
- 超类提取与抽象方法应用
-
在这段 Java 代码中,
Circle和Triangle类都有计算面积的方法,但计算方式不同。请提取一个超类,将计算面积的方法定义为抽象方法。class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double getArea() { return Math.PI * radius * radius; } } class Triangle { private double base; private double height; public Triangle(double base, double height) { this.base = base; this.height = height; } public double getArea() { return 0.5 * base * height; } }
-
- 超类提取与子类扩展
-
给定以下 Java 代码,
Student和Teacher类都有name和id属性,以及getInfo方法,但Teacher类还有subject属性。请提取一个超类,并让Teacher类扩展超类。class Student { private String name; private int id; public Student(String name, int id) { this.name = name; this.id = id; } public String getInfo() { return "Student: " + name + ", ID: " + id; } } class Teacher { private String name; private int id; private String subject; public Teacher(String name, int id, String subject) { this.name = name; this.id = id; this.subject = subject; } public String getInfo() { return "Teacher: " + name + ", ID: " + id + ", Subject: " + subject; } }
-
综合拓展练习题
- 多类超类提取与代码审查模拟
-
考虑一个简单的 Java 图形绘制系统,有
Line、Rectangle和Circle类,它们都有draw方法,并且都有color属性。请提取一个超类来重构这些类。 -
假设你完成了重构,请模拟一份简单的代码审查报告,指出重构后的优点和可能存在的潜在问题。
class Line { private String color; public Line(String color) { this.color = color; } public void draw() { System.out.println("Drawing a line with color: " + color); } } class Rectangle { private String color; public Rectangle(String color) { this.color = color; } public void draw() { System.out.println("Drawing a rectangle with color: " + color); } } class Circle { private String color; public Circle(String color) { this.color = color; } public void draw() { System.out.println("Drawing a circle with color: " + color); } }
-