subscribe

Dangers of mutual dependencies

Much like most people, I try work out my class dependencies through a top-down ‘waterfall’-ish approach. By attempting this, I think allows me to keep the structure very clear and understandable.

Example of top-down dependency approach

excuse my non-existent UML skills

In this example the Ingredient class (and subclasses) is Never aware of any Recipe classes, but only the other way round.

I try to apply the same model to instantiated objects and packages (groups of classes). When an object encapsulates another object, I attempt to make sure the sub-object object is not aware of the parent. When I design packages, I attempt to make sure 2 packages don’t require ‘each other’.

An example where this could be a problem is the following. Say, I have a ‘Database’ package. I want to log every database error to a ‘Log’ package. The ‘Log’ package has a couple of implementations, such as ‘Log_File’, ‘Log_Syslog’, but now I added ‘Log_Database’ to log any problems to the database.

Mutual dependency

Strictly speaking these two packages can no longer be separated and will always have to be used/downloaded together. As a bonus a database-error could occur while logging, resulting in an endless loop (or segmentation fault if you’re using PHP).

Another example of a mutual dependency:

file1.php:

<?php

include 'file2.php';

?>

file2.php:

<?php

include 'file1.php';

?>

You get the idea ;)

However, these types of situations are sometimes simply unavoidable (that’s why we have include_once). When they are needed, they should be implemented with care and consideration. The problem with the Log class could be fixed if the Log_Database is aware of errors thrown by itself, and this could be repackaged by creating by separating the Log_Database in a new package, depending on both the ‘Connection’ and ‘Log’ classes.

Editors note: this post was part of a much larger article around designing plug-in systems, but I lost inspiration and decided to post just this part.

Web mentions

Comments

  • Giorgio Sironi

    Giorgio Sironi

    IMHO to keep it simple the logger should not be aware of a connection of a connection interface: if connection is lost, an error is thrown and is logged with another query that will trigger another error etc...
    However, what about design the logger as a decorator?
    class Log_Database implements Connection
    {
    public function __construct($conn) { ... }
    public function __call($method, ...) {
    try {
    $this->conn->$method(...);
    } catch (Exception $e) {
    // write logs but assuring connection is open
    }
    }
    }
    This way the log package depends on the connection package, but not the other war. Sure it can be other solutions...