Traits to facilitate horizontal code reuse

suggest change

Let’s say we have an interface for logging:

interface Logger {
    function log($message);
}

Now say we have two concrete implementations of the Logger interface: the FileLogger and the ConsoleLogger.

class FileLogger implements Logger {
    public function log($message) {
        // Append log message to some file
    }
}

class ConsoleLogger implements Logger {
    public function log($message) {
        // Log message to the console
    }
}

Now if you define some other class Foo which you also want to be able to perform logging tasks, you could do something like this:

class Foo implements Logger {
    private $logger;

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function log($message) {
        if ($this->logger) {
            $this->logger->log($message);
        }
    }
}

Foo is now also a Logger, but its functionality depends on the Logger implementation passed to it via setLogger(). If we now want class Bar to also have this logging mechanism, we would have to duplicate this piece of logic in the Bar class.

Instead of duplicating the code, a trait can be defined:

trait LoggableTrait {
    protected $logger;

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function log($message) {
        if ($this->logger) {
            $this->logger->log($message);
        }
    }
}

Now that we have defined the logic in a trait, we can use the trait to add the logic to the Foo and Bar classes:

class Foo {
    use LoggableTrait;
}

class Bar {
    use LoggableTrait;
}

And, for example, we can use the Foo class like this:

$foo = new Foo();
$foo->setLogger( new FileLogger() );

//note how we use the trait as a 'proxy' to call the Logger's log method on the Foo instance
$foo->log('my beautiful message');

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you:



Table Of Contents