Использование делегатов или интерфейсов для разделения ведения журнала - Лучшие практики - C # - PullRequest
4 голосов
/ 20 июня 2009

Мои решения имеют несколько проектов, которые включают в себя несколько библиотек и один проект для пользовательского интерфейса. В настоящее время это приложение Windows Forms, и я использую log4net для регистрации. Этот проект пользовательского интерфейса имеет только ссылку на log4net, и этот проект поддерживает файлы конфигурации. Но я также хотел бы войти из моих библиотек.

Обычный способ сделать это - обернуть вызовы регистрации за интерфейсом. Создайте общий проект, который называется утилиты, и добавьте этот интерфейс в этот проект. Теперь этот проект может использоваться во всех проектах и ​​может использовать этот интерфейс для регистрации.

Я думаю об альтернативном дизайне, который включает в себя передачу делегатов и уменьшение связи и избегание ненужных интерфейсов.

Рассмотрим следующий класс из моей библиотеки.

public sealed class Foo
{
    Action<string> log;
    Action<string, Exception> logException;

    public Foo(Action<string> log, Action<string,Exception> logException)
    {
        this.log = log;
        this.logException = logException;
    }

    public void Work()
    {
        WL("Starting work");
        WL("Completed step1");
        .........
    }

    void WL(string message)
    {
        if(log != null) log(message);
    }

    void WL(string message, Exception exception)
    {
        if(logException != null) logException(message, exception);
    }
}

Теперь из вызывающего кода я могу легко передать метод регистрации. Что-то вроде

Foo foo = new Foo(message => Console.WriteLine(message), 
                (message, exception) => Console.WriteLine("{0},{1}", message, exception));
foo.Work();

Использовал консоль для объяснения, на самом деле я буду использовать здесь код регистрации.

1 - Вы считаете это лучшим решением? Я думаю, что это лучше, так как это более слабо связано.

2 - Существуют ли другие лучшие решения?

Этот является единственным связанным вопросом, который я нашел здесь

Есть мысли ...?

Ответы [ 7 ]

7 голосов
/ 20 июня 2009

Не используйте делегатов, если несколько подписей летят близко друг к другу. Все, что вы делаете, это избегаете определения классов и интерфейсов, которые будут иметь смысл. log4net предоставляет интерфейс ILog, который является отличным примером простого интерфейса оболочки, который вы можете передать.

Если вы собираетесь использовать каркас журналирования, особенно log4net, не оборачивайте его и не создавайте единую глобальную (статическую ИЛИ одноэлементную) точку входа. Я писал об этом раньше , и вас может заинтересовать вопрос о лучших практиках .

4 голосов
/ 20 июня 2009

У меня есть тонкий слой, который предоставляет API ведения журнала, очень похожий на Log4Net, который использует шаблон проектирования модели поставщика, чтобы позволить вам подключить любую подходящую среду ведения журнала. Я реализовал поставщиков для:

  • System.Diagnostics.Trace

  • log4net

  • EntLib

Это означает, что я могу использовать ведение журнала во всех своих приложениях без какой-либо прямой зависимости от конкретной структуры ведения журнала, а пользователи моих компонентов могут подключать свои собственные любимые средства ведения журнала.

2 голосов
/ 20 июня 2009

Мой совет - добавить ссылку на log4net во все ваши проекты, но оставить конфигурацию регистратора в проекте пользовательского интерфейса. Это по-прежнему дает вам возможность определять различные уровни ведения журнала для каждой сборки. Ведение журнала является настолько низким уровнем активности, а log4net является настолько зрелым продуктом, что я не стал бы тратить время, пытаясь придумать умное решение, чтобы удовлетворить «лучшие практики». Я мог бы даже возразить, если выпить пива или два, что ссылка на log4net ничем не отличается от ссылки на System.Core.

1 голос
/ 20 июня 2009

Google для "AOP logging".

Вот немного разговоров об этом от Айенде.

1 голос
/ 20 июня 2009

Цитата Джона С. «Простое почти всегда лучше умного» - ИМХО, ваше использование делегатов выглядит скорее последним.

Если вы хотите, чтобы библиотечные проекты регистрировались, им следует настроить и использовать собственный регистратор. Я бы не стал просить клиентов передавать регистратор (объект или интерфейс), который затем перемещается вглубь графа зависимости типа. Он просто немного загрязняет интерфейс ненужными параметрами объекта / интерфейса / делегата регистратора и т. Д. .

Если вы используете каркасы Log4XXX, я полагаю, что это подчеркивает концепцию " архитектуры иерархического ведения журнала " (имена, которые они придумывают в s / w;), где каждый тип / класс может поддерживать и записать в свой собственный файл журнала. Если ctor из Foo создает логгер внутри, мне бы это понравилось. А поскольку это настраивается, определенные клиенты могут изменить файлы конфигурации, чтобы перенаправить вывод также и в другое место.

1 голос
/ 20 июня 2009

Если у вас нет разных кусков кода, использующих разные каркасы журналирования, у меня был бы одиночный LogDispatcher или что-то похожее, на что будет обращаться весь код, который попытался бы выполнить регистрацию и, возможно, передавал бы уровень сообщений для определения правильной регистрации метод. Это предотвращает необходимость передачи делегатов для ведения журнала по всей базе кода и централизует весь код, отвечающий за политику ведения журнала.

Другой подход заключается в использовании фреймворка, подобного Log4Net . Даже если вы в конечном итоге не используете его, их дизайн подойдет для вашей собственной регистрации.

0 голосов
/ 20 января 2017

Так что ваша проблема - это одна, для которой мне скоро придется найти решение. Ответ по умолчанию - «Использовать инъекцию», но в этом случае это меньше инверсии управления и большее расширение зависимостей. Я думаю, что вы близки, вот мои мысли.

Плюсы вашего решения

Нет необходимости в дополнительных ссылках вашего класса или сборки, в которой он находится. Поскольку вы используете действия с общими типами, эти ссылки, вероятно, уже присутствуют.

Преимущество заключается в том, что 100% реализации ведения журнала оставлено сборке, которая внедряет ваши действия. Поэтому, если вы добавите log4Net в nLog, единственная ссылка на него будет там, где он реализован. Поэтому, если вы захотите переключиться позже, нужно обновить только эту сборку.

Обратное утверждение таково, если вы просто внедрите выбранный регистратор в каждый класс. Это означает, что вы должны добавить ссылку в каждом проекте в логгер. Даже если интерфейсы названы и реализованы одинаково, для разрешения необходимо иметь ссылку. В решениях, где у вас есть более 3 проектов, которые могут быть дорогостоящими, и вы будете иметь одинаковую стоимость в любое время, когда будете переключать регистраторы.

Возможное улучшение

В этом заключается красота вашего решения. Однако это может быть улучшено. Я обнаружил, что при введении вещей схожей функции или «аспекта» может иметь смысл поместить их в объект и вместо этого внедрить. Вы можете создать интерфейс с обоими вашими действиями и добавить бетоны, которые реализуют любую библиотеку, которую вы хотите. Это, опять-таки, оставило бы единственную ссылку на библиотеку журналов для одного проекта / сборки только с необходимостью добавить ссылку на ваш интерфейс к остальным.

Надеюсь, это поможет и удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...