То, что вы описали, называется в англоязычной литературе cross-cutting concerns - это такие области кода, которые решают не саму бизнес-задачу, а являются как бы перпендикулярными к ней, и относятся скорее к функционированию системы. Вы правы насчёт того, что добавлять логирование в саму функцию - это, наверное, не совсем правильный выбор, так как во-первых, функция начинает решать две задачи, что сразу же уменьшает время на понимание её работы, а во-вторых, возможно будут варианты вызова функции, при которых её выполнение логировать не обязательно.
Я бы, возможно, попробовал зайти к этой задаче со стороны паттерна Декоратор (дальше пишу на Java, так как этот язык для меня основной, но принципы должны быть понятны).
Определяем интерфейс:
interface ClientHandler {
void blockUser(User user);
void unblockUser(User user);
}
Дальше пишем реализацию, которая будет заниматься блокированием/разблокированием пользователя:
public final class ClientHandlerImpl implements ClientHandler {
public void blockUser(User user) {
// Логика блокирования пользователя
}
public void unblockUser(User user) {
// Логика разблокирования пользователя
}
}
А теперь ход конём: пишем декоратор, который будет оборачивать собой написанную нами имплементацию (и в аргументе конструктора пробрасываем ClientHandlerImpl):
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);
}
}
Дальше можно будет создать, например, фабрику ClientHandler'ов, которая по запросу будет возвращать нам новый инстанс ClientHandler'а:
public final class ClientHandlerFactory {
public static ClientHandler getClientHandler() {
return new ClientHandlerLoggingDecorator(new ClientHandler());
}
}
Чего мы добились:
1. Код логирования вынесен из реализации ClientHandler, если вам нужно будет изучить реализацию блокирования/разблокирования, вы просто открываете ClientHandlerImpl, и изучаете её.
2. (связано с предыдущим пунктом) Реализацию блокирования/разблокирования и логирования теперь можно менять независимо друг от друга.
3. Логирующее поведение становится скрытым для пользователей ClientHandler - они просто получают новый инстанс при обращении к фабрике, и используют его. Хотите отключить логирование? Меняете реализацию фабрики, и она начинает возвращать ClientHandlerImpl. Хотите сделать это поведение настраиваемым? Пишете дополнительный код, который на старте читает конфигурацию, и начинает использовать либо ту реализацию фабрики, которая возвращает ClientHandler, покрытый декоратором, либо реализацию, которая возвращает голый ClientHandler. Либо же зашиваете этот выбор внутрь самой фабрики.