Dependencies

So far, we've explored 3 types of relationships: composition, aggregation, and association. We've saved the simplest one for last: dependencies.

In casual conversation, we use the term dependency to indicate that an object is reliant upon another object for a given task. For example, if you need to send a package, you are dependent on a delivery service to transport it (but not otherwise). Plants are dependent upon sunlight to grow, in order to produce energy (but not otherwise).

A dependency occurs when one object invokes another object's functionality in order to accomplish some specific task. This is a weaker relationship than an association, but still, any change to the object being depended upon may break functionality in the (dependent) caller. A dependency is always a unidirectional relationship.

A good example of a dependency that you've already seen many times is std::ostream. Our classes that use std::ostream use it in order to accomplish the task of printing something to the console, but not otherwise.

For example:

#include <iostream>

class Coordinate
{
private:
    double m_x{};
    double m_y{};
    double m_z{};

public:
    Coordinate(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 Coordinate& coord); // Coordinate has a dependency on std::ostream here
};

std::ostream& operator<<(std::ostream& out, const Coordinate& coord)
{
    // Since operator<< is a friend of the Coordinate class, we can access Coordinate's members directly.
    out << "Coordinate(" << coord.m_x << ", " << coord.m_y << ", " << coord.m_z << ')';

    return out;
}

int main()
{
    Coordinate position{5.0, 10.0, 15.0};

    std::cout << position; // the program has a dependency on std::cout here

    return 0;
}

In the above code, Coordinate isn't directly related to std::ostream, but it has a dependency on std::ostream since operator<< uses std::ostream to print the Coordinate to the console.

Dependencies vs Association in C++

There's typically some confusion about what differentiates a dependency from an association.

In C++, associations are a relationship where one class keeps a direct or indirect "link" to the associated class as a member. For example, a Lawyer class has an array of pointers to its Clients as a member. You can always ask the Lawyer who its clients are. The Pilot class holds the tail number of the Aircraft the pilot object operates as an integer member. The Pilot always knows what Aircraft is associated with it.

Dependencies typically are not members. Rather, the object being depended on is typically instantiated as needed (like opening a file to write data to), or passed into a function as a parameter (like std::ostream in the overloaded operator<< above).

Summary

Dependency definition: A relationship where one object invokes another object's functionality to accomplish a specific task. The weakest relationship type, but changes to the depended-upon object may still break dependent code. Always unidirectional.

Dependency vs association: Dependencies are typically not stored as members. The depended-upon object is usually instantiated as needed or passed as a function parameter. Associations keep a direct or indirect link to the associated object as a member.

Common examples: The most common dependency you've seen is std::ostream. Classes use it to print output, but don't store it as a member or maintain a persistent relationship with it.

Characteristics: Dependencies are temporary relationships that exist only for the duration of a specific operation. Once the task is complete, the relationship ends.

Understanding dependencies helps identify coupling between classes and manage code maintainability. While dependencies are the weakest relationship type, they still create connections between parts of your codebase that should be considered during design and refactoring.