2009-11-25 2 views
3

Это скорее теоретический вопрос. Должна ли регистрация выполняться внутри класса, для которого основная цель не регистрируется?Должна ли регистрироваться в классе, который не предназначен для регистрации?

Вот простой интерфейс для всего, что будет заготовлять расчет по числу.

public interface ICalculation { 
    public int calculate(int number); 
} 

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

public class ReallyIntenseCalculation : ICalculation { 
    private readonly ILogger log; 

    public ReallyIntenseCalculation() : this(new DefaultLogger()) { 
    } 

    public ReallyIntenseCalculation(ILogger log) { 
     this.log = log; 
     log.Debug("Instantiated a ReallyIntenseCalculation."); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = DoTheDirtyWork(number); 
     log.Info(number + " resulted in " + answer); 
     return answer; 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
     log.Debug("A little bit of granular logging sprinkled in here."); 
    } 
} 

После удаления всего кода протоколирования из ReallyIntenseCalculation, код теперь имеет то, что, как представляется, ясно единый ответственность.

public class ReallyIntenseCalculation : ICalculation { 

    public int calculate(int number) { 
     return DoTheDirtyWork(number); 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
    } 
} 

Хорошо, поэтому мы удалили способность ReallyIntenseCalculation регистрировать его внутренности. Как мы можем найти способ экстернализировать эту функциональность. Введите шаблон декоратора.

Создав класс, который украшает ICalculation, мы можем добавить запись в микс, но это приведет к компрометации некоторых более подробных журналов, которые происходят в частных методах ReallyIntenseCalculation.

public class CalculationLoggingDecorator : ICalculation { 
    private readonly ICalculation calculation; 
    private readonly ILogger log; 

    public CalculationLoggingDecorator(ICalculation calculation, ILogger log) { 
     this.calculation = calculation; 
     this.log = log; 
     log.Debug("Instantiated a CalculationLoggingDecorator using " + calculation.ToString()); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = calculation.calculate(number); 
     log.Info(number + " resulted in " + answer); 
    } 
} 

Каковы другие возможные плюсы и минусы использования декоратора лесозаготовок?

ответ

3

Я думаю, что это спорно. Можно сделать вывод, что регистрация является частью этой единственной ответственности.

Это, я думаю, вы думаете с точки зрения cross-cutting concerns, что-то, с чем связано aspect-oriented programming. Фактически, код регистрации является каноническим примером для АОП. Возможно, вы захотите рассмотреть структуру AOP, такую ​​как aspect#.

Преимущества такого рода, конечно же, это разложимость, повторное использование и разделение проблем.

0

Сколько у вас регистраторов? Я бы сделал реализацию конструктора калькулятора в частном порядке экземпляр экземпляра журнала, используя метод Singleger Logger или Logger (так что регистрация или не ведение журнала не является частью общедоступного API калькулятора, а является частью реализации калькулятора).

+0

Если это часть реализации, то это должен быть параметр конструктора. В противном случае, как кто-то смотрит на API, который должен знать, и что, если пользователь не хочет использовать другой регистратор. –

3

Я согласен с Джейсоном в том, что это более сложная проблема.

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

AOP - лучший выбор здесь, а для .NET PostSharp - лучший вариант.

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

public class ReallyIntenseCalculation : ICalculation { 

    public int calculate(int number) { 
     return DoTheDirtyWork(number); 
    } 

    private int DoTheDirtyWork(int number) { 
     // crazy math happens here 
    } 
} 

public class CalculationLoggingDecorator : ICalculation { 
    ICalculation calculation; 
    ILogger log; 
    public CalculationLogging() { 
     this.calculation = new ReallyIntenseCalculation() ; 
     this.log = SomeLogger(...); 
     log.Debug("Initialized a CalculationLoggingDecorator using " + calculation.ToString()); 
    } 

    public int calculate(int number) { 
     log.Debug("Some debug logging.") 
     var answer = calculation.calculate(number); 
     log.Info(number + " resulted in " + answer); 
    } 
} 

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

Это больше работы, и АОП предпочтительнее, но DI может быть альтернативой.

ОБНОВЛЕНИЕ: Основано на комментарии.

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

AOP был бы лучшим подходом, но концепция является твердой продажей некоторым компаниям, чем DI.

Возможно, у вас есть два класса для каждой реализации, но ваша информация о регистрации по-прежнему удаляется из этих других классов, каждый класс вводится через DI, поэтому вы можете иметь два файла app.config, один со всеми создавая классы регистрации и один для производства, чтобы упростить вашу жизнь. Второй класс просто регистрирует и настраивает информацию, хотя, я считаю, это не слишком большая работа, но вы потеряли тонкую детализацию ведения журнала.

+0

Что делать, если я должен был ввести больше классов, реализующих ICalculations, например ReallySimpleCalculation и AbsurdlyComplexCalculation. Разве код выше не был бы подвержен классовому взрыву? – JaredCacurak