0

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?

MarcDefiant
  • 6,649
  • 6
  • 29
  • 49

2 Answers2

1

AOP is a great tool the could allow you to separate logging from your business logic.

One of the most common scenarios to use AOP is logging so it might worth take a look.

You could analyze if there is any framework that fits your needs play with it to see how it goes.

On this link you could take a look of how logging could be implemented using postsharp, a C# AOP framework

Claudio Redi
  • 67,454
  • 15
  • 130
  • 155
  • That sounds nice, but do you have some kind of example, or could you point me to a location to start from? – MarcDefiant Mar 01 '13 at 12:35
  • I'm not very familiar with PHP, you should try to find a framework specific for it. A search `AOP php` on google may help – Claudio Redi Mar 01 '13 at 12:42
  • I'll definitely do that. But note: the question isn't tagged with PHP, so it would be awesome if you could post an example in a language you're familiar with, just to get an basic idea. This would also improve the value of this question/answer for future readers. – MarcDefiant Mar 01 '13 at 12:47
  • Added a link to a well known AOP framework on c#, please let me know if that helps – Claudio Redi Mar 01 '13 at 14:09
1

Your approach is correct and answers to the inversion of control principle. However, as you've stated, the logger is a cross-cutting concern for an application and has nothing to do with the Position entity. In other words: It's purely technical and we don't want to know about it.

I see two options here:

1) You reference all your loggers via a Manager class of some sort, and use the one you require at the class level. This LoggingManager must be known to all classes in the solution that require logging functionalities. I've done this often when working with external Logging Frameworks (ex: Log4Net).

2) Or you can request the logger at runtime from a higher entity (Service) that will provide the required Logger. Using a Dependancy Injection Framework or Aspect Oriented Programming-style class configuration would remove the choice of the Logger Type (or Factory) from the class itself.

Unfortunately I'm not well versed in PHP, so I can't provide you with a code example. But I did find this good example of AOP in C# : http://visualstudiomagazine.com/articles/2011/05/12/wccsp_aspect-oriented-programming.aspx

Peter
  • 56
  • 2
  • Found a few PHP AOP frameworks listed on another thread on StackOverflow: http://stackoverflow.com/questions/4738282/are-there-any-working-aspect-oriented-php-libraries – Peter Mar 01 '13 at 13:13