16.5 – Зависимости
На данный момент мы рассмотрели 3 типа связей: композиция, агрегация и ассоциация. Самый простой вариант мы оставили напоследок: зависимости.
В обычном разговоре мы используем термин зависимость, чтобы указать, что объект в заданной задаче зависит от другого объекта. Например, если вы сломаете ногу, для передвижения вы будете зависеть от костылей. В задаче выращивания фруктов и размножения цветы зависят от пчел, которые опыляют их.
Зависимость возникает, когда один объект для выполнения определенной задачи обращается к функционалу другого объекта. Это более слабая связь, чем ассоциация, но всё же любое изменение объекта, от которого есть зависимость, может нарушить работоспособность (зависимого) вызывающего объекта. Зависимость – это всегда однонаправленная связь.
Хорошим примером зависимости, которую вы уже видели много раз, является std::cout
(типа std::ostream
). Наши классы, которые используют std::cout
, используют его для выполнения задачи вывода чего-либо в консоль.
Например:
#include <iostream>
class Point
{
private:
double m_x, m_y, m_z;
public:
Point(double x=0.0, double y=0.0, double z=0.0): m_x(x), m_y(y), m_z(z)
{
}
friend std::ostream& operator<< (std::ostream &out, const Point &point);
};
std::ostream& operator<< (std::ostream &out, const Point &point)
{
// Поскольку operator<< является другом класса Point,
// мы можем напрямую обращаться к членам класса Point.
out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")";
return out;
}
int main()
{
Point point1(2.0, 3.0, 4.0);
std::cout << point1;
return 0;
}
В приведенном выше коде Point
не имеет прямой связи с std::cout
, но имеет зависимость от std::cout
, поскольку operator<<
использует std::cout
для вывода Point
в консоль.
Зависимости и ассоциации в C++
Обычно существует небольшая путаница в том, что отличает зависимость от ассоциации.
В C++ ассоциации – это связь между двумя классами на уровне класса. То есть один класс поддерживает прямую или косвенную «ссылку» на связанный класс как член. Например, класс Doctor
содержит в качестве члена массив указателей на своих пациентов (объекты класса Patient
). Вы всегда можете спросить доктора, кто его пациенты. Класс водителя Driver
в члене типа int
содержит идентификатор автомобиля (объекта класса Car
), которым владеет объект Driver
. Объект Driver
всегда знает, какой объект Car
с ним ассоциируется.
Зависимости обычно не представлены на уровне класса, то есть зависимый объект не привязан как член. Скорее, объект, от которого есть зависимость, обычно создается при необходимости (например, открытие файла для записи данных) или передается в функцию в качестве параметра (например, std::ostream
в перегруженном operator<<
, как в примере выше).
Немного юмора
Зависимости (взято с xkcd):
Но мы с вами, конечно же, знаем, что это на самом деле рефлексивная ассоциация!