H
H
Hint2017-01-11 19:35:20
Programming
Hint, 2017-01-11 19:35:20

Who should log? Function or the one who calls it?

There is a site with users, there are moderators. There is an action log (subject, object, action, description, time, etc.), there is a function to add an entry to the log. Suppose you need to log all actions to block and unblock users (users can be blocked both by moderators and some kind of automatic procedures). There is a blocking function. There are about ten calls to this function. Question. Who should call the logging function: the blocking function or the function that calls it? If the function itself, then there is less code (no need to duplicate the code many times), but extra parameters are added, it starts to do something that is not quite its own, etc. If the one who calls the function, then this is a lot of identical lines of code. Plus, you can still call the blocking function and forget to add the logging code. How right?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Artem Gapchenko, 2017-01-12
@Hint

What you described is called cross-cutting concerns in the English literature - these are areas of code that do not solve the business problem itself, but are, as it were, perpendicular to it, and are more related to the functioning of the system. You are right about the fact that adding logging to the function itself is probably not the right choice, because firstly, the function begins to solve two tasks, which immediately reduces the time to understand its work, and secondly, there may be options for calling a function, in which its execution is not required to be logged.
I would probably try to access this task from the side of the Decorator pattern (further I write in Java, since this language is the main one for me, but the principles should be clear).
Define an interface:

interface ClientHandler {
  void blockUser(User user);
  void unblockUser(User user);
}

Next, we write an implementation that will deal with blocking / unblocking the user:
public final class ClientHandlerImpl implements ClientHandler {
  public void blockUser(User user) {
    // Логика блокирования пользователя
  }

  public void unblockUser(User user) {
    // Логика разблокирования пользователя
  }
}

And now the knight’s move: we write a decorator that will wrap the implementation we wrote (and forward ClientHandlerImpl in the constructor argument):
public final class ClientHandlerLoggingDecorator implements ClientHandler {
  private final ClientHandler handler;

  public ClientHandlerLoggingDecorator(final ClientHandler handler) {
    this.handler = handler;
  }

  public void blockUser(User user) {
    Log.d("User " + user.getName() + " blocked!")
    handler.blockUser(user);
  }

  public void unblockUser(User user) {
    Log.d("User " + user.getName() + " unblocked!")
    handler.unblockUser(user);
  }
}

Next, it will be possible to create, for example, a ClientHandler factory, which, upon request, will return a new ClientHandler instance to us:
public final class ClientHandlerFactory {
  public static ClientHandler getClientHandler() {
    return new ClientHandlerLoggingDecorator(new ClientHandler());
  }
}

What we have achieved:
1. The logging code is taken out of the ClientHandler implementation, if you need to study the blocking/unblocking implementation, you just open the ClientHandlerImpl and study it.
2. (related to the previous paragraph) The implementation of blocking / unblocking and logging can now be changed independently of each other.
3. Logging behavior becomes hidden for ClientHandler users - they just get a new instance when accessing the factory, and use it. Do you want to disable logging? You change the factory implementation, and it starts returning ClientHandlerImpl. Want to make this behavior customizable? You write additional code that reads the configuration at the start and starts using either the factory implementation that returns a ClientHandler covered with a decorator, or an implementation that returns a bare ClientHandler. Or you sew this choice inside the factory itself.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question