您现在的位置是:首页 >学无止境 >Java代码重构学习笔记-处理概括关系网站首页学无止境

Java代码重构学习笔记-处理概括关系

Logan_addoil 2024-06-17 10:14:41
简介Java代码重构学习笔记-处理概括关系

Pull Up Field(字段上移)

它的目的是将子类中相同的字段上移到父类中,以消除冗余代码并提高代码重用性。

在面向对象的程序设计中,子类可以继承父类中的字段和方法。如果多个子类中都有相同的字段,那么我们可以通过将这些字段上移至父类中,从而避免重复定义,并提高代码重用性。

例如,假设我们有一个父类 Person 和两个子类 StudentTeacher,它们分别包含了相同的 nameage 字段:

public class Person {
    // ...
}

public class Student extends Person {
    private String name;
    private int age;
    // ...
}

public class Teacher extends Person {
    private String name;
    private int age;
    // ...
}

可以通过将 nameage 字段上移至父类 Person 中,从而消除冗余代码:

public class Person {
    private String name;
    private int age;
    // ...
}

public class Student extends Person {
    // ...
}

public class Teacher extends Person {
    // ...
}

在上面的代码中,我们将 nameage 字段上移至了父类 Person 中,从而避免了重复定义。现在,子类 StudentTeacher 可以直接访问这些字段,而不需要再次定义。

通过使用字段上移技术,我们可以消除冗余代码并提高代码的重用性。但是需要注意的是,在使用此项技术时,需要确保子类中的字段类型和访问修饰符与父类完全一致,否则可能会产生编译错误或者运行时错误。

Pull Up Method(函数上移)

它的目的是将两个或多个子类相同的函数上移至它们的共同父类中,从而消除冗余代码并提高代码重用性。

在面向对象的程序设计中,子类可以继承父类中的函数。如果多个子类中都有相同的函数,那么我们可以通过将这些函数上移至父类中,从而避免重复定义,并提高代码重用性。

例如,假设我们有一个父类 Shape 和两个子类 RectangleCircle,它们分别包含了相同的 area() 方法:

public class Shape {
    // ...
}

public class Rectangle extends Shape {
    // ...
    public double area() {
        // 计算矩形面积
        double length = getLength();
        double width = getWidth();
        return length * width;
    }
}

public class Circle extends Shape {
    // ...
    public double area() {
        // 计算圆形面积
        double radius = getRadius();
        return Math.PI * radius * radius;
    }
}

可以通过将 area() 方法上移至父类 Shape 中,从而消除冗余代码:

public class Shape {
    // ...
    public double area() {
        return 0.0; // 默认实现
    }
}

public class Rectangle extends Shape {
    // ...
    public double area() {
        // 计算矩形面积
        double length = getLength();
        double width = getWidth();
        return length * width;
    }
}

public class Circle extends Shape {
    // ...
    public double area() {
        // 计算圆形面积
        double radius = getRadius();
        return Math.PI * radius * radius;
    }
}

在上面的代码中,我们将 area() 方法上移至了父类 Shape 中,并提供了默认实现。现在,子类 RectangleCircle 可以覆盖这个方法以提供自己的具体实现。

通过使用函数上移技术,我们可以消除冗余代码并提高代码的重用性。但是需要注意的是,在使用此项技术时,需要确保子类中的方法名、参数列表以及返回类型与父类完全一致,否则可能会产生编译错误或者运行时错误。

Pull Up Constructor Body(构造函数本体上移)

它的目的是将两个或多个子类相同的构造函数中的初始化代码上移至它们的共同父类中,从而消除冗余代码并提高代码重用性。

在面向对象的程序设计中,子类可以继承父类中的构造函数。如果多个子类中都有相同的初始化操作,那么我们可以通过将这些初始化操作上移至父类中,从而避免重复定义,并提高代码重用性。

例如,假设我们有一个父类 Animal 和两个子类 CatDog,它们分别包含了相同的初始化操作:

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        // 公共的初始化操作
        System.out.println("创建了一只名为" + name + "、年龄为" + age + "岁的动物");
    }
}

public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
        // 猫的特殊初始化操作
        System.out.println("这是一只猫");
    }
}

public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
        // 狗的特殊初始化操作
        System.out.println("这是一只狗");
    }
}

可以通过将构造函数中的公共初始化操作上移至父类 Animal 中,从而消除冗余代码:

public class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        // 公共的初始化操作
        System.out.println("创建了一只名为" + name + "、年龄为" + age + "岁的动物");
    }
}

public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
        // 猫的特殊初始化操作
        System.out.println("这是一只猫");
    }
}

public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
        // 狗的特殊初始化操作
        System.out.println("这是一只狗");
    }
}

在上面的代码中,我们将构造函数中的公共初始化操作上移至了父类 Animal 中。现在,所有子类都可以直接调用父类的构造函数,并且公共的初始化操作只需要定义一次即可。

通过使用构造函数本体上移技术,我们可以消除冗余代码并提高代码的重用性。但是需要注意的是,在使用此项技术时,需要确保子类中的构造函数调用父类构造函数的语法和参数列表与父类完全一致,否则可能会产生编译错误或者运行时错误。

Push Down Method(函数下移)

它的目的是将父类中只有部分子类需要使用的函数下移至它们各自的子类中,从而提高代码的封装性和灵活性。

在面向对象的程序设计中,子类可以继承父类中的函数。如果某个函数只有部分子类需要使用,那么我们可以通过将这个函数下移至它们各自的子类中,从而提高代码的封装性和灵活性。

例如,假设有一个父类 Animal 和两个子类 CatDog,它们都包含了 eat() 方法,但只有 Cat 需要特殊实现:

public class Animal {
    public void eat() {
        System.out.println("动物在进食");
    }
}

public class Cat extends Animal {
    public void eat() {
        System.out.println("猫在吃鱼");
    }
}

public class Dog extends Animal {
    // Dog 不需要特殊实现 eat() 方法
}

可以通过将 eat() 方法下移至 Cat 类中,从而提高代码的封装性和灵活性:

public class Animal {
    // eat() 方法被下移至 Cat 类中
}

public class Cat extends Animal {
    public void eat() {
        System.out.println("猫在吃鱼");
    }
}

public class Dog extends Animal {
    // Dog 不需要特殊实现 eat() 方法
}

在上面的代码中,我们将 eat() 方法下移至了 Cat 类中。现在,只有 Cat 类包含了这个方法,而其他子类和父类不再包含这个方法。

通过使用函数下移技术,我们可以提高代码的封装性和灵活性。但是需要注意的是,在使用此项技术时,需要确保将函数下移至适当的子类中,并确保其他子类和父类不再需要这个方法,否则可能会影响代码的正确性和可维护性。

Push Down Field(字段下移)

它的目的是将父类中只有部分子类需要使用的字段下移至它们各自的子类中,从而提高代码的封装性和灵活性。

在面向对象的程序设计中,子类可以继承父类中的字段。如果某个字段只有部分子类需要使用,那么我们可以通过将这个字段下移至它们各自的子类中,从而提高代码的封装性和灵活性。

例如,假设有一个父类 Animal 和两个子类 CatDog,它们都包含了 name 字段,但只有 Cat 需要特殊实现:

public class Animal {
    protected String name;
    // 其他字段和方法
}

public class Cat extends Animal {
    private String coatColor;
    // 其他字段和方法
}

public class Dog extends Animal {
    // Dog 不需要特殊实现 name 字段
}

可以通过将 name 字段下移至 Cat 类中,从而提高代码的封装性和灵活性:

public class Animal {
    // name 字段被下移至 Cat 类中
}

public class Cat extends Animal {
    private String name;
    private String coatColor;
    // 其他字段和方法
}

public class Dog extends Animal {
    // Dog 不需要特殊实现 name 字段
}

在上面的代码中,我们将 name 字段下移至了 Cat 类中。现在,只有 Cat 类包含了这个字段,而其他子类和父类不再包含这个字段。

通过使用字段下移技术,我们可以提高代码的封装性和灵活性。但是需要注意的是,在使用此项技术时,需要确保将字段下移至适当的子类中,并确保其他子类和父类不再需要这个字段,否则可能会影响代码的正确性和可维护性。

Extract Subclass (提炼子类)

它的目的是将一个类中的一部分行为提取出来,创建一个新的子类,从而提高代码的可读性、可维护性和可扩展性。

在面向对象的程序设计中,一个类通常会包含多种不同的行为。如果一个类中的某些行为可以被归纳为一个单独的概念或职责,并且有其他的类也可能需要这些行为,那么就可以使用“提炼子类”技术。

例如,假设有一个 Employee 类,其中包含了 name, monthlySalarycommission 字段,以及一个 calculatePay() 方法来计算月薪和佣金:

public class Employee {
    protected String name;
    protected double monthlySalary;
    protected double commission;

    public double calculatePay() {
        return monthlySalary + commission;
    }

    // 其他字段和方法
}

现在,我们决定将销售人员(Saler)和非销售人员(Non-Saler)分别作为两个子类来处理。可以使用“提炼子类”技术,将销售人员的 calculatePay() 方法提取到 Saler 子类中:

public class Employee {
    protected String name;
    protected double monthlySalary;

    public double calculatePay() {
        return monthlySalary;
    }

    // 其他字段和方法
}

public class Saler extends Employee {
    protected double commission;

    public double calculatePay() {
        return monthlySalary + commission;
    }

    // 其他字段和方法
}

public class NonSaler extends Employee {
    // 其他字段和方法
}

在上面的代码中,我们使用“提炼子类”技术,将 Saler 子类提取出来,并将 commission 字段和相应的 calculatePay() 方法移动到了 Saler 类中。

通过使用“提炼子类”技术,我们可以将行为进行分类并将其分配到适当的子类中,从而提高代码的可读性、可维护性和可扩展性。但是需要注意的是,在使用此项技术时,需要确保子类与父类之间的关系和功能的正确性,以确保代码的正确性和可维护性。

Extract Superclass(提炼超类)

它的目的是将两个或多个类中的公共特征提取到一个新的父类中,从而消除重复代码,提高代码可维护性和可扩展性。

在面向对象的程序设计中,两个或多个类可能会存在许多重复的成分。如果这些类都具有某些公共特征,则可以使用“提炼超类”技术将这些特征提取到一个新的父类中。

例如,假设有两个类 RectangleCircle,它们都包含了相似的属性和方法,如下所示:

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

    public Rectangle() {
    }

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

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

    // 其他方法
}

public class Circle {
    protected double radius;

    public Circle() {
    }

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }

    // 其他方法
}

现在,我们想要将 width, heightradius 字段提取出来,并将它们放置到一个称为 Shape 的父类中,同时让 RectangleCircle 类成为 Shape 父类的子类。可以使用“提炼超类”技术来实现此目的:

public abstract class Shape {
    protected double width;
    protected double height;
    protected double radius;

    public abstract double getArea();

    // 其他方法
}

public class Rectangle extends Shape {
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

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

    // 其他方法
}

public class Circle extends Shape {
    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }

    // 其他方法
}

在上面的代码中,我们创建了一个 Shape 父类,并将 width, heightradius 字段移动到了这个父类中。然后,我们让 RectangleCircle 类成为 Shape 父类的子类,并在这两个类中分别实现了 getArea() 方法。

通过使用“提炼超类”技术,我们可以消除重复代码并提高代码的可维护性和可扩展性。但是需要注意的是,在使用此项技术时,需要确保新的父类与原先的子类之间的关系和功能的正确性,以确保代码的正确性和可维护性。

Extract Interface(提炼接口)

它的目的是将一个类中的公共方法提取出来并定义成一个新的接口,从而增强代码的可扩展性和灵活性。

在面向对象的程序设计中,一个类通常会包含多个方法。如果这些方法具有一个公共的特征或职责,并且这个特征或职责与其他类之间存在交集,则可以使用“提炼接口”技术,将这些方法提取出来并定义成一个新的接口。

例如,假设有一个 Person 类,其中包含了多个方法,如下所示:

public class Person {
    private String name;
    private int age;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    // 其他方法
}

现在,我们决定将 Person 类中的 getName()getAge()getAddress() 方法提取并定义为一个新的接口 Readable,以便其他类也可以使用这些方法。可以使用“提炼接口”技术来实现此目的:

public interface Readable {
    String getName();
    int getAge();
    String getAddress();
}

public class Person implements Readable {
    private String name;
    private int age;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    // 其他方法
}

在上面的代码中,我们使用了“提炼接口”技术将 getName()getAge()getAddress() 方法提取并定义为一个新的接口 Readable。然后,我们让 Person 类实现这个接口,并在 Person 类中实现这些方法。

通过使用“提炼接口”技术,我们可以增强代码的可扩展性和灵活性,使其他类也可以使用这些公共方法。但是需要注意的是,在使用此项技术时,需要确保抽象出来的接口具有良好的设计、可维护、可扩展和易用性等特性,以确保代码的正确性和可维护性。

Collapse Hierarchy(折叠继承体系)

它的目的是将一个继承体系中的某些层次折叠到一起,从而简化代码结构,提高代码的可读性和可维护性。

在面向对象的程序设计中,一个类可以通过继承另一个类来获取其属性和方法。如果一个继承体系过于复杂,包含了太多层次的继承关系,会导致代码难以理解和维护。此时可以使用“折叠继承体系”技术来简化继承体系,减少继承层次,提高代码的可读性和可维护性。

例如,假设有一个 Animal 类,它有多个子类,如 BirdFishMammal 等等,其中 BirdFish 类都有 swim() 方法,而 MammalBird 类都有 run() 方法。现在我们考虑将 Bird 类和 Fish 类的共同方法抽象出来,并将 Mammal 类和 Bird 类的共同方法抽象出来,以简化继承体系,可以使用“折叠继承体系”技术来实现此目的:

public abstract class Animal {
    public abstract void move();
}

public interface Swimmable {
    void swim();
}

public interface Runnable {
    void run();
}

public class Bird extends Animal implements Swimmable, Runnable {
    public void move() {
        fly();
    }
    public void swim() {
        // swim logic
    }
    public void run() {
        // run logic
    }
    private void fly() {
        // fly logic
    }
}

public class Fish extends Animal implements Swimmable {
    public void move() {
        swim();
    }
    public void swim() {
        // swim logic
    }
}

public class Mammal extends Animal implements Runnable {
    public void move() {
        // move logic
    }
    public void run() {
        // run logic
    }
}

在上面的代码中,我们使用了“折叠继承体系”技术将 Bird 类和 Fish 类的共同方法抽象出来,并将 Mammal 类和 Bird 类的共同方法抽象出来。具体地,我们定义了 SwimmableRunnable 两个接口,并让 BirdFishMammal 类实现对应的接口。然后,我们将 Bird 类的 swim() 方法和 Mammal 类的 move() 方法抽象到了对应的接口中,从而简化了继承体系。

通过使用“折叠继承体系”技术,我们可以简化继承体系,减少继承层次,提高代码的可读性和可维护性。但是需要注意的是,在使用此项技术时,需要确保折叠的继承层次具有合理的关系和正确的功能,以确保代码的正确性和可维护性。

Form Tem Plate Method(塑造模板函数)

它的目的是将一个算法框架从子类中提取出来,并将其定义为一个模板方法,在模板方法中只保留算法框架的结构,而将算法的具体实现交给子类去完成。

在面向对象的程序设计中,有时候我们需要为一组相关的操作或算法定义一个共同的框架,但对于每个具体的操作或算法,其细节和实现并不相同。此时可以使用“塑造模板函数”技术将这些操作或算法的共同部分抽象出来,并将其定义为一个模板方法,然后让子类实现模板方法中的具体算法部分,从而达到代码复用和减少重复代码的目的。

例如,假设我们有一个 DataImporter 类,它包含了多个方法来导入数据,并且对于每种数据类型都有不同的导入逻辑,如下所示:

public class DataImporter {
    public void importFromCsv() {
        // import from csv logic
    }

    public void importFromExcel() {
        // import from excel logic
    }

    public void importFromJson() {
        // import from json logic
    }

    // 其他方法
}

现在,我们考虑将导入数据的过程抽象出来,将其定义为一个模板方法,具体的导入逻辑由子类来完成。可以使用“塑造模板函数”技术来实现此目的:

public abstract class DataImporter {
    public void importData() {
        readData();
        parseData();
        validateData();
        saveData();
    }

    protected abstract void readData();

    protected abstract void parseData();

    protected abstract void validateData();

    protected abstract void saveData();
}

public class CsvImporter extends DataImporter {
    protected void readData() {
        // read from csv logic
    }

    protected void parseData() {
        // parse csv data logic
    }

    protected void validateData() {
        // validate csv data logic
    }

    protected void saveData() {
        // save csv data logic
    }
}

public class ExcelImporter extends DataImporter {
    protected void readData() {
        // read from excel logic
    }

    protected void parseData() {
        // parse excel data logic
    }

    protected void validateData() {
        // validate excel data logic
    }

    protected void saveData() {
        // save excel data logic
    }
}

public class JsonImporter extends DataImporter {
    protected void readData() {
        // read from json logic
    }

    protected void parseData() {
        // parse json data logic
    }

    protected void validateData() {
        // validate json data logic
    }

    protected void saveData() {
        // save json data logic
    }
}

在上面的代码中,我们使用了“塑造模板函数”技术将导入数据的过程抽象出来,并定义为一个模板方法 importData()。然后,我们将 CsvImporterExcelImporterJsonImporter 三个子类分别实现模板方法中的具体算法部分,从而使得这些子类可以复用父类的模板方法,并在其基础上实现不同的导入逻辑。

通过使用“塑造模板函数”技术,我们可以将一组相关的操作或算法的共同部分抽象出来,并将其定义为一个模板方法,以达到减少重复代码和提高代码的可维护性的目的。但是需要注意的是,在使用此项技术时,需要确保模板方法具有良好的设计、可扩展和易用性等特性,以确保代码的正确性和可维护性。

Replace Inheritance with Delegation(以委托取代继承)

它的目的是通过将一个类的行为委托给另一个类来实现相同的功能,从而避免使用继承机制带来的大量副作用和限制。

通常情况下,当一个类需要使用另一个类的某些功能时,可以使用继承机制来继承该类并获得其所有属性和方法。这种做法虽然简单明了,但也带来了一些问题,例如:

  1. 继承机制会产生紧耦合关系,使得代码的复用性和可维护性差。
  2. 继承会暴露父类的实现细节,而子类可能并不需要这些实现细节。
  3. 多重继承可能会产生命名冲突和歧义。

为了解决这些问题,可以使用“Replace Inheritance with Delegation”(以委托取代继承)技术。具体来说,可以创建一个新的类,将原有类中需要使用的功能抽象出来,然后在新的类中添加一个指向原有类的引用,并将需要使用的功能委托给原有类中的对应方法来实现。

例如,假设有两个类 A 和 B,其中类 A 继承自类 B。现在我们需要让类 C 拥有类 A 的某些功能,但不想使用继承机制来实现。这时可以使用“Replace Inheritance with Delegation”(以委托取代继承)技术来解决。具体实现方式如下:

  1. 在类 C 中添加一个指向类 A 的引用。
  2. 将需要使用的功能从类 A 中抽象出来,并实现到类 C 中。
  3. 在类 C 中调用类 A 的相应方法,实现委托关系。

代码示例:

class A {
  public:
    void operation1() { /* 实现逻辑1 */ }
    void operation2() { /* 实现逻辑2 */ }
};

class B : public A {
  // 父类 A 的实现
};

class C {
  private:
    A* a;

  public:
    void operation1() { a->operation1(); }
    // operation2 的实现委托给了 A 中的相应方法。
    void operation2() { a->operation2(); }
};

通过使用“Replace Inheritance with Delegation”(以委托取代继承)技术,可以避免继承机制导致的副作用和限制,并提高代码的可读性和可维护性。同时,也可以实现更灵活的代码组合和重用。

Replace Delegation with Inheritance(以继承取代委托)

它的目的是通过将一个类与其委托对象之间的委托关系转化为父类与子类之间的继承关系,来简化代码和提高代码的可读性。相对于“Replace Inheritance with Delegation”(以委托取代继承),这种技术可以使得代码更加简单、容易理解和修改。

举例来说,假设有一个文字处理软件,其中包含了多种类型的文本对象(例如,段落、文本框、图像等)。这些文本对象都具有一些共同的属性和方法,例如,它们都可以设置颜色、字体大小、对齐方式等,也都能够进行复制、粘贴、拖动等操作。

为了实现这些功能,通常可以使用委托机制来实现。例如,每个文本对象都可以包含一个控制器对象,用于处理各种操作和事件。然而,随着软件规模的增大,使用委托机制会带来许多问题,例如:

  1. 委托链可能会变得很长,难以维护和理解。
  2. 如果需要添加新的文本对象类型,可能需要修改大量的控制器类。
  3. 在实现时,可能产生重复的代码和功能,影响代码的可维护性和扩展性。

为了解决这些问题,可以使用“Replace Delegation with Inheritance”(以继承取代委托)技术。具体实现方式如下:

  1. 创建一个表示文本对象的父类,并在其中实现共同的属性和方法。
  2. 让各个文本对象类型都继承自该父类,从而获得共同的功能。
  3. 在子类中添加特有的属性和方法,并进行相应的重写和实现。

代码示例:

class TextObject {
  public:
    virtual void setColor(Color color) { /* 实现逻辑1 */ }
    virtual void setFontSize(int size) { /* 实现逻辑2 */ }
    virtual void setAlignment(Alignment alignment) { /* 实现逻辑3 */ }
    virtual void copy() { /* 实现逻辑4 */ }
    virtual void paste() { /* 实现逻辑5 */ }
    virtual void drag() { /* 实现逻辑6 */ }
    virtual void drop() { /* 实现逻辑7 */ }
};

class Paragraph : public TextObject {
  // 实现 Paragraph 特有的属性和方法
};

class TextBox : public TextObject {
  // 实现 TextBox 特有的属性和方法
};

class Image : public TextObject {
  // 实现 Image 特有的属性和方法
};

通过使用“Replace Delegation with Inheritance”(以继承取代委托)技术,可以将委托关系转换为继承关系,从而简化代码结构和提高代码的可读性和维护性。同时,也可以更加方便地添加新的文本对象类型,并实现更灵活的组合和重用。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。