类与类之间的关系

类与类之间的六种关系


继承关系

定义:

继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。

类图:

代码:

// 定义一个基类,名为Animal
class Animal {
    // 基类的属性
    protected String name;

    // 基类的构造方法
    public Animal(String name) {
        this.name = name;
    }

    // 基类的方法
    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// 定义一个派生类,继承自Animal类
class Dog extends Animal { // 使用extends关键字来表示继承
    private String breed; // 派生类的私有属性

    // 派生类的构造方法
    public Dog(String name, String breed) {
        super(name); // 调用基类的构造方法
        this.breed = breed;
    }

    // 重写基类的方法
    @Override  // 使用@Override注解来明确表示重写
    public void eat() {
        System.out.println(name + ", a " + breed + ", is eating.");
    }

    // 派生类特有的方法
    public void bark() {
        System.out.println(name + " barks!");
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        // 创建基类的对象
        Animal animal = new Animal("Generic Animal");
        animal.eat(); // 调用基类的方法

        // 创建派生类的对象
        Dog dog = new Dog("Buddy", "Labrador");
        dog.eat(); // 调用派生类重写的方法
        dog.bark(); // 调用派生类特有的方法
    }
}

实现关系

定义:

实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。

类图:

代码:

// 定义一个接口,名为Actionable
interface Actionable {
    void performAction();
}

// 定义一个类,名为Student,它实现了Actionable接口
class Student implements Actionable {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    // 实现Actionable接口中的performAction方法
    @Override
    public void performAction() {
        System.out.println(name + " is performing an action.");
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        // 创建Student类的对象
        Student student = new Student("Alice");

        // 调用Student对象的performAction方法
        student.performAction();
    }
}

依赖关系

定义:

简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。

类图:

代码:

// 被依赖类:Printer
class Printer {
    public void print(String content) {
        System.out.println("Printing: " + content);
    }
}

// 依赖类:Document
class Document {
    private Printer printer; // 依赖于Printer类

    // 构造方法,注入Printer对象
    public Document(Printer printer) {
        this.printer = printer;
    }

    // 使用Printer对象的方法
    public void displayContent(String content) {
        printer.print(content); // 依赖关系在这里体现
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        // 创建Printer对象
        Printer printer = new Printer();

        // 创建Document对象,并注入Printer对象
        Document document = new Document(printer);

        // 使用Document对象,间接使用Printer对象
        document.displayContent("Hello, World!");
    }
}

关联关系

定义:

关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记。

类图:

代码:

// 定义一个类,名为Person,表示一个人
class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

// 定义另一个类,名为Car,表示一辆车
class Car {
    private String model;
    private Person owner; // Car类与Person类存在关联关系

    public Car(String model) {
        this.model = model;
    }
    
    public void setOwner(Person owner) {
        this.owner = owner;
    }
    
    public Person getOwner() {
        return owner;
    }
    
    public void drive() {
        if (owner != null) {
            System.out.println(owner.getName() + " is driving a " + model);
        } else {
            System.out.println("No owner is driving the " + model);
        }
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        // 创建Person对象
        Person person = new Person("Alice");

        // 创建Car对象,并设置其owner属性
        Car car = new Car("Toyota Camry");
        car.setOwner(person);

        // 调用Car对象的drive方法
        car.drive();
    }
}

聚合关系

定义:

聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,聚合关系以空心菱形加实线箭头表示。

类图:

代码:

// 定义一个类,名为University,表示一所大学
class University {
    private String name;
    private Department[] departments; // 聚合关系:大学包含多个系

    public University(String name) {
        this.name = name;
    }

    public void addDepartment(Department department) {
        if (departments == null) {
            departments = new Department[1];
            departments[0] = department;
        } else {
            Department[] temp = new Department[departments.length + 1];
            System.arraycopy(departments, 0, temp, 0, departments.length);
            temp[departments.length] = department;
            departments = temp;
        }
    }

    public void showDetails() {
        System.out.println("University: " + name);
        if (departments != null) {
            for (Department department : departments) {
                System.out.println("Department: " + department.getName());
            }
        }
    }
}

// 定义另一个类,名为Department,表示一个系
class Department {
    private String name;
    private Professor[] professors; // 聚合关系:系包含多个教授

    public Department(String name) {
        this.name = name;
    }

    public void addProfessor(Professor professor) {
        if (professors == null) {
            professors = new Professor[1];
            professors[0] = professor;
        } else {
            Professor[] temp = new Professor[professors.length + 1];
            System.arraycopy(professors, 0, temp, 0, professors.length);
            temp[professors.length] = professor;
            professors = temp;
        }
    }

    public String getName() {
        return name;
    }
}

// 定义一个类,名为Professor,表示一位教授
class Professor {
    private String name;

    public Professor(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        // 创建教授对象
        Professor professor1 = new Professor("John Doe");
        Professor professor2 = new Professor("Jane Smith");

        // 创建系对象,并添加教授
        Department computerScience = new Department("Computer Science");
        computerScience.addProfessor(professor1);
        computerScience.addProfessor(professor2);

        // 创建大学对象,并添加系
        University university = new University("Tech University");
        university.addDepartment(computerScience);

        // 显示大学及其系和教授的详细信息
        university.showDetails();
    }
}

组合关系

定义:

组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,组合关系以实心菱形加实线箭头表示。

类图:

代码:

// 定义一个类,名为Brain,表示一个人的大脑
class Brain {
    public void think() {
        System.out.println("The brain is thinking.");
    }

    // 当销毁大脑时执行的方法
    protected void finalize() throws Throwable {
        System.out.println("The brain has been destroyed.");
        // 调用父类的finalize()方法
        super.finalize();
    }
}

// 定义另一个类,名为Person,表示一个人
class Person {
    private Brain brain; // 组合关系:一个人包含一个大脑

    public Person() {
        this.brain = new Brain(); // 人创建时,创建其大脑
    }

    // 当销毁人时执行的方法
    @Override
    protected void finalize() throws Throwable {
        System.out.println("The person is being destroyed.");
        // 销毁大脑
        brain = null; // 清除大脑引用,允许垃圾回收
        // 调用父类的finalize()方法
        super.finalize();
    }

    public void work() {
        brain.think(); // 人工作时,大脑在思考
    }
}

// 主类,包含main方法,用于执行程序
public class Main {
    public static void main(String[] args) {
        Person person = new Person(); // 创建人实例
        person.work(); // 人工作

        // 为了演示目的,手动调用finalize方法
        // 注意:在实际应用中,finalize方法的调用时机不确定,不应该依赖它来释放资源
        person.finalize();
    }
}

耦合度

面向对象编程中的六种关系,按照耦合度从高到低排列,通常如下:

  1. 继承(Inheritance):

    • 耦合度高。子类与父类之间存在强关联,父类的变化可能直接影响到子类。
  2. 实现(Implementation):

    • 耦合度中等。类实现接口时,需要实现接口的所有方法,但接口本身不包含实现。
  3. 聚合(Aggregation):

    • 耦合度较低。一个类是另一个类的成员,但可以独立存在。整体对部分的了解较少。
  4. 组合(Composition):

    • 耦合度较高。一个类包含另一个类的实例,部分的生命周期依赖于整体,但部分可以被替换。
  5. 关联(Association):

    • 耦合度根据具体情况而定。一个类引用另一个类,但不一定拥有它。可以是松耦合,如通过方法参数传递。
  6. 依赖(Dependency):

    • 耦合度低。一个类在方法级别或类级别上使用另一个类,但不持有其引用,通常是通过局部变量或方法参数。

耦合度的解释:

  • 继承: 子类继承父类的所有属性和方法,耦合度最高。父类的修改可能直接影响所有子类。
  • 实现: 类实现接口时,耦合度中等。接口的修改可能需要类进行相应的修改,但实现类可以独立于接口的其他实现类存在。
  • 聚合: 类之间存在“拥有”关系,但整体对部分的了解较少,部分可以独立于整体存在,耦合度较低。
  • 组合: 类之间存在“部分-整体”的强关联,部分的生命周期依赖于整体,耦合度较高。但部分可以被替换,提供了一定程度的灵活性。
  • 关联: 两个类之间存在使用关系,但这种使用可能是临时的或通过方法参数传递,耦合度可以很低。
  • 依赖: 最低耦合度。一个类可能在某个方法中使用另一个类的对象,但不持有对该对象的持久引用。

最高设计原则


"高内聚,低耦合"是软件设计中两个非常重要的原则,它们帮助创建易于维护、扩展和理解的系统。

  1. 高内聚(High Cohesion):

    • 内聚性是指单个模块或类内部元素彼此关联的程度。高内聚意味着类或模块中的元素紧密相关,并且共同协作以完成一个单一、明确定义的任务。
    • 高内聚的类或模块通常具有以下特点:
      • 功能单一:每个类或模块只负责一项任务或功能。
      • 易于理解:由于功能集中,理解和维护变得更加容易。
      • 易于测试:可以独立地测试单个模块的功能。
  2. 低耦合(Low Coupling):

    • 耦合度是指不同模块之间相互依赖的程度。低耦合意味着各个模块之间的依赖关系尽可能少,每个模块可以独立地更改和更新,而不影响其他模块。
    • 低耦合的系统通常具有以下特点:
      • 易于修改:模块间的依赖性小,修改一个模块不太可能影响到其他模块。
      • 易于扩展:可以添加新功能而不影响现有代码。
      • 重用性高:独立模块可以被重用在不同的项目中。

实现高内聚和低耦合的策略:

  • 单一职责原则(SRP): 每个类应该只有一个引起它变化的原因,即每个类只负责一项任务。
  • 开放/封闭原则(OCP): 类应该对扩展开放,对修改关闭。这意味着设计应该允许新增功能,而不是修改现有代码。
  • 里氏替换原则(LSP): 子类应该能够替换其基类而不影响程序的正确性。
  • 接口隔离原则(ISP): 不应该强迫客户依赖于它们不使用的方法。
  • 依赖倒置原则(DIP): 高层模块不应依赖于低层模块,两者都应该依赖于抽象;抽象不应依赖于细节,细节应依赖于抽象。
  • 使用设计模式: 如工厂模式、策略模式、观察者模式等,可以帮助降低模块间的耦合度。
  • 依赖注入(DI): 通过外部注入依赖,而不是让模块自己创建或查找依赖,可以降低模块间的耦合度。
  • 使用接口和抽象类: 定义清晰的接口,让模块间的交互基于抽象而不是具体实现。
posted @ 2024-08-02 10:01  墨澜  阅读(50)  评论(0编辑  收藏  举报