Let's say you're writing a "bigger" application and you want to log certain errors in your classes. Now almost every class needs access to a Logger.
One simple solution would be the following (PHP, but that doesn't matter):
class SomeClass {
public function someMethod() {
// stuff ...
Logger::log("something happened");
// stuff ...
}
}
The problem with this approach is it makes every class which uses the Logger depend on it and you can't simply use the same class in an other project without the Logger. It's tightly coupled. And you can't change the way the message is logged without changing the implementation of SomeClass
.
A "little" upgrade of the approach above would be the following:
class SomeClass {
private $logger;
public function __construct(/* stuff */) {
$this->logger = LoggerFactory::getLogger();
}
public function someMethod() {
// stuff ...
$this->logger->log("something happened");
// stuff ...
}
}
This doesn't really solve the problem because SomeClass
now simply depends on the LoggerFactory
and it's tightly coupled to it. The good thing about that is, that LoggerFactory
could now return different instances of the Logger
class. So, if you wanted to change the way the messages are logged, you wouldn't need to change the implementation of SomeClass
.
An other approach would be using dependency injection:
class SomeClass {
private $logger;
public function __construct(Logger $logger, /* stuff */) {
$this->logger = $logger;
}
public function someMethod() {
// stuff ...
$this->logger->log("something happened");
// stuff ...
}
}
Now this doesn't couple SomeClass
to any specific class. It simply needs an class which implements the Logger
interface. But the problem I have with this is, that you need to pass a Logger
instance to the constructor whenever you create an object.
$object = new Position($this->logger, $x, $y);
The other problem I have with this is that, the Logger isn't really part of the Position
class like $x
and $y
. It's just an utility.
How do you handle such situations best? What would be the optimal compromise between coupling and cohesion in this case?