Возвращаясь к объектно-ориентированным принципам, одна из ключевых особенностей объекта - его поведение и состояние . Я мог бы представить сценарий, в котором обработчику журнала может потребоваться поддерживать какое-то состояние (имя файла журнала, соединение с БД и т. Д.), Но также может быть аргумент для обработчика журнала, которому не нужно заботиться о состоянии.
Если вашей зависимости нужно управлять собственным состоянием, используйте подходящий объект (скорее интерфейс).
Если ваша зависимость имеет только поведение, а не состояние, тогда может подойти делегат, хотя некоторым людям может быть удобнее использовать надлежащий объект (интерфейс) в любом случае, так как позже было бы легче добавить к нему управление состоянием, если необходимо.
Преимущество делегатов в том, что они CRAZY просты для насмешки с помощью лямбда-выражений :) (хотя интерфейсы тоже довольно просты для насмешки)
Теперь, конечно, любой делегат все еще может быть каким-то нормальным методом для некоторого нормального объекта, и этот метод может полностью иметь поведение, которое влияет на состояние объекта, и, безусловно, есть веские причины для этого, но вы приближаетесь точка, в которой может иметь смысл просто взять зависимость от всего объекта, а не только от одного из его методов.
Далее по этому пути внедрение делегатов также может быть способом применения Принцип разделения интерфейса , так что вы можете убедиться, что ваша система не зависит от того, что она не использует.
Еще одно замечание о делегатах ...
Почти никогда нет веской причины определять свой собственный тип делегата. Большинство вариантов использования соответствуют типам Func<>
и Action<>
C # (и событиям, но это другая проблема).
В вашем случае ваш MainApplication
конструктор не должен принимать Window1.LogHandler
в качестве параметра, а просто Action<string>
. Тогда вы просто позвоните с:
MainApplication app = new MainApplication(ConsoleLogHandler, new ToughSecurityManager());
или аналогичный, поскольку метод ConsoleLogHandler
уже соответствует сигнатуре Action<string>
.
И в своем тесте вы просто запишите это с помощью:
MainApplication app = new MainApplication(x => { /*Do nothing*/ }, new MySecurityManagerStub());
или даже лучше:
int timesCalled;
MainApplication app = new MainApplication(x => { timesCalled++ }, new MySecurityManagerStub());
Затем вы можете проверить, что MainApplication вызывал метод ровно столько раз, сколько вы намеревались.