Использование делегатов вместо интерфейсов для развязки. Отличная идея? - PullRequest
2 голосов
/ 19 мая 2009

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

На определенных этапах элемент управления приложения GUI передается другому классу, например, основной элемент управления переключается с экрана входа в систему на экран ввода данных после аутентификации пользователя. Различные классы должны использовать функциональность объектов, принадлежащих элементу управления верхнего уровня. В прошлом я просто передавал объекты подчиненным элементам управления или создавал интерфейс. В последнее время я перешел к передаче делегатов методов вместо целых объектов по двум основным причинам:

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

Ниже приведен несколько упрощенных примеров кода:

delegate bool LoginDelegate(string username, string password);
delegate void UpdateDataDelegate(BizData data);
delegate void PrintDataDelegate(BizData data);

class MainScreen {
    private MyNetwork m_network;
    private MyPrinter m_printer;

    private LoginScreen m_loginScreen;
    private DataEntryScreen m_dataEntryScreen;

    public MainScreen() {
        m_network = new Network();
        m_printer = new Printer();

        m_loginScreen = new LoginScreen(m_network.Login);
        m_dataEntryScreen = new DataEntryScreen(m_network.Update, m_printer.Print);
    }
}

class LoginScreen {
    LoginDelegate Login_External;

    public LoginScreen(LoginDelegate login) {
        Login_External = login
    }
}

class DataEntryScreen {
    UpdateDataDelegate UpdateData_External;
    PrintDataDelegate PrintData_External;

    public DataEntryScreen(UpdateDataDelegate updateData, PrintDataDelegate printData) {
        UpdateData_External = updateData;
        PrintData_External = printData;
    }
}

Мой вопрос заключается в том, что, хотя я предпочитаю этот подход, и он имеет смысл для меня, как следующий разработчик найдет его? В примерах и с открытым исходным кодом интерфейсы кода C # являются предпочтительным подходом для разделения, тогда как этот подход использования делегатов больше склоняется к функциональному программированию. Могу ли я заставить последующих разработчиков ругаться под нос за то, что для них является нелогичным?

Ответы [ 2 ]

2 голосов
/ 19 мая 2009

Это интересный подход. Вы можете обратить внимание на две вещи:

  1. Как упоминал Филипп, когда у вас есть много методов для определения, вы получите большой конструктор. Это вызовет глубокую связь между классами. Еще один или менее делегат потребует от каждого изменения подписи. Вы должны рассмотреть возможность сделать их общедоступными и использовать некоторые DI-фреймворки.

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

Кажется, здесь важно количество делегатов.

1 голос
/ 19 мая 2009

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

  • "При юнит-тестировании намного проще издеваться над методом, чем классом" . Большинство поддельных фреймворков для c # построены на идее насмешки над типом. В то время как многие могут издеваться над методами, образцы и документация (и фокус) обычно связаны с типами. Мокать интерфейс одним методом так же легко и просто, как и метод.

  • "Это делает код более читабельным, документируя в конструкторе классов, какие именно методы используются подчиненными классами." Также есть свои минусы - если классу требуется несколько методов, конструкторы становятся большими ; и как только подчиненному классу нужно новое свойство или метод, вместо простого изменения интерфейса, вы также должны добавить его ко всем конструкторам классов в цепочке.

Я не говорю, что это плохой подход любым способом - передача функций, а не типов, четко определяет, что вы делаете, и может снизить сложность вашей объектной модели. Тем не менее, в c # ваш следующий разработчик, вероятно, увидит это как странное или запутанное (в зависимости от уровня квалификации). Сочетание OO и функциональных подходов, вероятно, вызовет удивление у большинства разработчиков, с которыми вы будете работать.

...