关联关系

  • 1.关联关系
  • 1.1单向关联
  • 1.2双向关联
  • 1.3自关联
  • 2.聚合关系
  • 3.组合关系
  • 4.依赖关系
  • 5.继承关系
  • 6 实现关系



1.关联关系

关联关系是面向对象编程中的一个概念,用于描述两个类之间的联系。在关联关系中,一个类的对象与另一个类的对象之间存在某种关联,但它们的关系不如组合关系那样密切。这种关联可以是单向的,也可以是双向的。
在关联关系中,一个类的对象可以作为另一个类的成员变量,或者作为方法的参数或返回值等形式出现。这种关系可以用来描述现实世界中的各种情况,例如学生和课程之间的关系、订单和产品之间的关系等。

关联关系的特点包括:

  1. 双向关系:两个类之间的关联可以是双向的,即彼此互相关联。
  2. 多重性:关联关系中的对象之间可能存在一对一、一对多、多对一或多对多等不同的关系。
  3. 独立性:关联关系中的两个对象的生命周期通常是独立的,它们之间的存在并不依赖于彼此。

关联关系在代码实现中通常通过成员变量、方法参数或返回值来体现,以反映出两个类之间的关联性。在使用关联关系时,需要注意避免过度耦合,以提高代码的灵活性和可维护性。

1.1单向关联

在下面的示例中,Order 类中使用了指针 const Customer* customer 来表示订单和客户之间的单向关联关系。在 main 函数中创建订单对象时,将客户对象的地址传入订单对象的构造函数中,从而建立了单向关联关系。这样,订单对象可以通过指针访问关联的客户对象的信息,但客户对象无法直接通过订单对象访问关联的订单信息,从而形成了单向关联关系。

#include <iostream>
#include <vector>
#include <string>

using namespace std;

// 定义客户类
class Customer {
private:
    int customerId;
    string customerName;

public:
    Customer(int id, const string& name) : customerId(id), customerName(name) {}

    int getCustomerId() const {
        return customerId;
    }

    const string& getCustomerName() const {
        return customerName;
    }
};

// 定义订单类
class Order {
private:
    int orderId;
    string orderDate;
    const Customer* customer; // 使用指针表示关联关系

public:
    Order(int id, const string& date, const Customer* cust) : orderId(id), orderDate(date), customer(cust) {}

    int getOrderId() const {
        return orderId;
    }

    const string& getOrderDate() const {
        return orderDate;
    }

    const Customer* getCustomer() const {
        return customer;
    }
};

int main() {
    // 创建客户对象
    Customer customer1(1, "John Doe");

    // 创建订单对象,并传入客户对象的地址作为关联
    Order order1(1001, "2024-03-30", &customer1);

    // 获取订单信息以及关联的客户信息
    cout << "Order ID: " << order1.getOrderId() << endl;
    cout << "Order Date: " << order1.getOrderDate() << endl;
    cout << "Customer ID: " << order1.getCustomer()->getCustomerId() << endl;
    cout << "Customer Name: " << order1.getCustomer()->getCustomerName() << endl;

    return 0;
}

1.2双向关联

双向关联关系意味着两个类之间的关联是双向的,每个类都能够直接访问关联的对象。在 C++ 中实现双向关联关系时,可以在每个类中使用指针或引用来表示另一个类的关联。在下面的示例中,Customer 类和 Order 类分别存储了彼此的指针,并提供了方法来访问关联的对象。在创建订单对象时,将订单添加到客户的订单列表中,从而建立了双向关联关系。

#include <iostream>
#include <vector>
#include <string>

using namespace std;

// 前置声明,避免互相引用时出错
class Order;

// 定义客户类
class Customer {
private:
    int customerId;
    string customerName;
    vector<Order*> orders; // 用于存储关联的订单指针

public:
    Customer(int id, const string& name) : customerId(id), customerName(name) {}

    int getCustomerId() const {
        return customerId;
    }

    const string& getCustomerName() const {
        return customerName;
    }

    // 添加订单到客户的订单列表中
    void addOrder(Order* order) {
        orders.push_back(order);
    }

    // 获取客户的所有订单
    const vector<Order*>& getOrders() const {
        return orders;
    }
};

// 定义订单类
class Order {
private:
    int orderId;
    string orderDate;
    Customer* customer; // 用于存储关联的客户指针

public:
    Order(int id, const string& date, Customer* cust) : orderId(id), orderDate(date), customer(cust) {
        // 在订单构造函数中,将订单添加到客户的订单列表中
        customer->addOrder(this);
    }

    int getOrderId() const {
        return orderId;
    }

    const string& getOrderDate() const {
        return orderDate;
    }

    // 获取订单关联的客户
    Customer* getCustomer() const {
        return customer;
    }
};

int main() {
    // 创建客户对象和订单对象
    Customer customer1(1, "John Doe");
    Order order1(1001, "2024-03-30", &customer1);

    // 获取订单关联的客户以及客户关联的订单
    cout << "Order ID: " << order1.getOrderId() << endl;
    cout << "Order Date: " << order1.getOrderDate() << endl;
    cout << "Customer ID: " << order1.getCustomer()->getCustomerId() << endl;
    cout << "Customer Name: " << order1.getCustomer()->getCustomerName() << endl;

    cout << "Customer's Orders:" << endl;
    const vector<Order*>& customer1Orders = customer1.getOrders();
    for (const auto& order : customer1Orders) {
        cout << " - Order ID: " << order->getOrderId() << ", Order Date: " << order->getOrderDate() << endl;
    }

    return 0;
}

1.3自关联

自关联是指一个类与自身建立关联关系。在 C++ 中,可以通过在类中使用指针或引用来实现自关联。在下面的示例中,Node 类表示一个简单的链表节点,包含一个整数数据成员和一个指向下一个节点的指针。通过在节点类中使用指针来指向下一个节点,实现了节点之间的自关联关系。在 main 函数中,创建了三个节点对象,并通过调用 setNext 方法建立了节点之间的关联关系。然后,通过遍历链表输出节点数据,最后释放了动态分配的内存。

#include <iostream>
#include <string>

using namespace std;

// 定义节点类
class Node {
private:
    int data;
    Node* next; // 指向下一个节点的指针

public:
    Node(int val) : data(val), next(nullptr) {}

    // 设置下一个节点
    void setNext(Node* node) {
        next = node;
    }

    // 获取下一个节点
    Node* getNext() const {
        return next;
    }

    // 获取节点数据
    int getData() const {
        return data;
    }
};

int main() {
    // 创建节点对象
    Node* node1 = new Node(1);
    Node* node2 = new Node(2);
    Node* node3 = new Node(3);

    // 建立节点之间的关联关系
    node1->setNext(node2);
    node2->setNext(node3);

    // 遍历节点链表
    Node* current = node1;
    while (current != nullptr) {
        cout << "Node data: " << current->getData() << endl;
        current = current->getNext();
    }

    // 释放动态分配的内存
    delete node1;
    delete node2;
    delete node3;

    return 0;
}

2.聚合关系

聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系。在聚合关系中,一个类作为另一个类的成员,并且这个成员对象的生命周期可以独立于包含它的类。在 C++ 中,聚合关系通常通过在类中创建成员对象或指针来实现。聚合关系的特点是,成员可以脱离于整体对象独立存在,比如学校与老师的关系,学校包含老师,但是就算学校没了,老师依然可以存在。
在下面的示例中,Teacher 类表示老师,School 类表示学校。在 School 类中,使用了一个 vector 来存储学校包含的老师对象的指针,这体现了学校和老师之间的聚合关系。通过 addTeacher 方法可以向学校对象中添加老师。通过 getTeacherNames 方法可以获取学校所有老师的姓名。

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 老师类
class Teacher {
private:
    string name;

public:
    Teacher(const string& n) : name(n) {}

    const string& getName() const {
        return name;
    }
};

// 学校类
class School {
private:
    string name;
    vector<Teacher*> teachers; // 聚合关系,学校包含了多个老师对象的指针

public:
    School(const string& n) : name(n) {}

    // 添加老师
    void addTeacher(Teacher* teacher) {
        teachers.push_back(teacher);
    }

    // 获取学校所有老师的姓名
    vector<string> getTeacherNames() const {
        vector<string> teacherNames;
        for (const auto& teacher : teachers) {
            teacherNames.push_back(teacher->getName());
        }
        return teacherNames;
    }

    // 获取学校名称
    const string& getName() const {
        return name;
    }
};

int main() {
    // 创建老师对象
    Teacher mrSmith("Mr. Smith");
    Teacher msJohnson("Ms. Johnson");

    // 创建学校对象
    School highSchool("High School");

    // 将老师添加到学校中
    highSchool.addTeacher(&mrSmith);
    highSchool.addTeacher(&msJohnson);

    // 输出学校名称
    cout << "School name: " << highSchool.getName() << endl;

    // 输出学校所有老师的姓名
    cout << "Teachers in " << highSchool.getName() << ":" << endl;
    for (const auto& teacherName : highSchool.getTeacherNames()) {
        cout << teacherName << endl;
    }

    return 0;
}

3.组合关系

组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在在组合关系中,一个类包含另一个类的对象,并且包含的对象的生命周期与包含它的类的生命周期相同。在 C++ 中,组合关系通常通过在类中直接创建成员对象来实现。
在下面的示例中,Wheel 类表示轮子,Car 类表示汽车。Car 类包含了四个 Wheel 类的对象作为成员变量,这实现了 Car 类对 Wheel 类的组合关系。在 main 函数中,创建了一个汽车对象 myCar,并输出了汽车的型号以及每个轮子的尺寸。

#include <iostream>
#include <string>

using namespace std;

// 定义轮子类
class Wheel {
private:
    int size;

public:
    Wheel(int s) : size(s) {}

    int getSize() const {
        return size;
    }
};

// 定义汽车类,其中包含轮子对象
class Car {
private:
    string model;
    Wheel frontLeftWheel;
    Wheel frontRightWheel;
    Wheel rearLeftWheel;
    Wheel rearRightWheel;

public:
    Car(const string& m, int wheelSize) :
        model(m),
        frontLeftWheel(wheelSize),
        frontRightWheel(wheelSize),
        rearLeftWheel(wheelSize),
        rearRightWheel(wheelSize) {}

    const string& getModel() const {
        return model;
    }

    const Wheel& getFrontLeftWheel() const {
        return frontLeftWheel;
    }

    const Wheel& getFrontRightWheel() const {
        return frontRightWheel;
    }

    const Wheel& getRearLeftWheel() const {
        return rearLeftWheel;
    }

    const Wheel& getRearRightWheel() const {
        return rearRightWheel;
    }
};

int main() {
    // 创建汽车对象
    Car myCar("Toyota", 18);

    // 输出汽车信息及轮子信息
    cout << "Car Model: " << myCar.getModel() << endl;
    cout << "Front Left Wheel Size: " << myCar.getFrontLeftWheel().getSize() << " inches" << endl;
    cout << "Front Right Wheel Size: " << myCar.getFrontRightWheel().getSize() << " inches" << endl;
    cout << "Rear Left Wheel Size: " << myCar.getRearLeftWheel().getSize() << " inches" << endl;
    cout << "Rear Right Wheel Size: " << myCar.getRearRightWheel().getSize() << " inches" << endl;

    return 0;
}

4.依赖关系

依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。在依赖关系中,一个类使用另一个类的对象作为参数或者局部变量,但是它们之间没有拥有关系。依赖关系通常体现在方法参数的传递或局部变量的使用上。
在下面的示例中,Engine 类表示引擎,Car 类表示汽车。在 Car 类的 drive 方法中,使用了 Engine 类的对象作为参数,这体现了 Car 类对 Engine 类的依赖关系。在 main 函数中,创建了一个汽车对象 myCar 和一个引擎对象 myEngine,然后调用了汽车对象的 drive 方法,并将引擎对象作为参数传入。

#include <iostream>
#include <string>

using namespace std;

// 定义引擎类
class Engine {
public:
    void start() {
        cout << "Engine started." << endl;
    }

    void stop() {
        cout << "Engine stopped." << endl;
    }
};

// 定义汽车类,其中使用了引擎对象作为参数
class Car {
private:
    string model;

public:
    Car(const string& m) : model(m) {}

    // 方法中使用了引擎对象作为参数,体现了依赖关系
    void drive(Engine& engine) {
        cout << "Driving the " << model << " car." << endl;
        engine.start();
        // Driving logic...
        engine.stop();
    }
};

int main() {
    // 创建汽车对象和引擎对象
    Car myCar("Toyota");
    Engine myEngine;

    // 调用汽车对象的方法,传入引擎对象作为参数
    myCar.drive(myEngine);

    return 0;
}

5.继承关系

继承关系是耦合度最大的一种,也是我们最常看见的一种,子类基础父类。在关联关系中,一个类和另一个类有联系,但它们之间的关系不如组合关系密切。通常,一个类的对象可以作为另一个类的成员变量,但它们之间的生命周期可以是独立的。继承关系则是一种类与类之间的特殊关系,其中一个类(子类)可以继承另一个类(父类)的属性和方法。
在下面的示例中,Wheel 类表示轮子,Car 类表示汽车,SportsCar 类表示跑车。Car 类中包含了四个 Wheel 对象的指针,这体现了关联关系。而 SportsCar 类继承自 Car 类,这体现了继承关系。在 main 函数中,创建了一个跑车对象 mySportsCar,并输出了跑车的型号、是否有涡轮增压以及每个轮子的尺寸。

#include <iostream>
#include <string>

using namespace std;

// 定义轮子类
class Wheel {
private:
    int size;

public:
    Wheel(int s) : size(s) {}

    int getSize() const {
        return size;
    }
};

// 定义汽车类,其中关联了轮子对象
class Car {
private:
    string model;
    Wheel* wheels[4]; // 关联了四个轮子对象的指针

public:
    Car(const string& m, int wheelSize) : model(m) {
        for (int i = 0; i < 4; ++i) {
            wheels[i] = new Wheel(wheelSize);
        }
    }

    ~Car() {
        for (int i = 0; i < 4; ++i) {
            delete wheels[i];
        }
    }

    const string& getModel() const {
        return model;
    }

    const Wheel* getWheel(int index) const {
        if (index >= 0 && index < 4) {
            return wheels[index];
        } else {
            return nullptr;
        }
    }
};

// 定义跑车类,继承自汽车类
class SportsCar : public Car {
private:
    bool turbo;

public:
    SportsCar(const string& m, int wheelSize, bool t) : Car(m, wheelSize), turbo(t) {}

    bool hasTurbo() const {
        return turbo;
    }
};

int main() {
    // 创建跑车对象
    SportsCar mySportsCar("Ferrari", 20, true);

    // 输出跑车信息及轮子信息
    cout << "Sports Car Model: " << mySportsCar.getModel() << endl;
    cout << "Has Turbo: " << (mySportsCar.hasTurbo() ? "Yes" : "No") << endl;
    for (int i = 0; i < 4; ++i) {
        cout << "Wheel " << i + 1 << " Size: " << mySportsCar.getWheel(i)->getSize() << " inches" << endl;
    }

    return 0;
}

6 实现关系

实现关系(Implementation Relationship)是面向对象编程中描述类与接口之间的关系。在实现关系中,类实现了一个或多个接口,它承诺实现接口中定义的所有抽象方法和属性。
在下面示例中,Transportation 是一个接口类,其中声明了纯虚函数 move(),表示交通工具的移动操作。Car 和 Ship 分别是实现了 Transportation 接口的类,它们都必须提供 move() 方法的具体实现。在 main() 函数中,我们创建了 Car 和 Ship 的对象,并通过这些对象调用了 move() 方法,展示了实现关系的用法。

#include <iostream>

// 定义交通工具接口
class Transportation {
public:
    virtual void move() = 0; // 抽象方法,表示交通工具的移动操作
};

// 汽车类实现了交通工具接口
class Car : public Transportation {
public:
    void move() override {
        std::cout << "Car is moving on the road." << std::endl;
    }
};

// 船类实现了交通工具接口
class Ship : public Transportation {
public:
    void move() override {
        std::cout << "Ship is sailing on the sea." << std::endl;
    }
};

int main() {
    Car car;
    Ship ship;

    // 通过实现了接口的对象调用接口方法
    car.move();
    ship.move();

    return 0;
}