C# 使用委托而不是接口进行解耦。好主意?

C# 使用委托而不是接口进行解耦。好主意?,c#,unit-testing,functional-programming,readability,decoupling,C#,Unit Testing,Functional Programming,Readability,Decoupling,在编写GUI应用程序时,我使用一个顶级类来“控制”或“协调”应用程序。顶级类将负责协调诸如初始化网络连接、处理应用程序范围的UI操作、加载配置文件等事情 在GUI应用程序的某些阶段,控件被移交给不同的类,例如,一旦用户进行身份验证,主控件将从登录屏幕切换到数据输入屏幕。不同的类需要使用顶级控件拥有的对象的功能。在过去,我只是将对象传递给从属控件或创建一个接口。最近,我改为传递方法委托,而不是传递整个对象,主要原因有两个: 在进行单元测试时,模拟方法要比模拟类容易得多 它通过在类构造函数中准确记

在编写GUI应用程序时,我使用一个顶级类来“控制”或“协调”应用程序。顶级类将负责协调诸如初始化网络连接、处理应用程序范围的UI操作、加载配置文件等事情

在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#代码接口中,代码接口是解耦的首选方法,而这种使用委托的方法更倾向于函数式编程。我很可能会让后来的开发人员低声咒骂什么是违反直觉的方法吗?

虽然我肯定能看到使用委托而不是界面的积极一面,但我不同意你的两个要点:

  • “在进行单元测试时,模拟方法比模拟类容易得多”。大多数c#的模拟框架都是围绕模拟类型的思想构建的。虽然许多方法都可以模拟,但样本和文档(以及焦点)通常围绕类型。用一个方法模拟接口与模拟一个方法一样容易或更容易

  • “通过在类构造函数中准确记录下级类使用的方法,它使代码更具可读性。”还有它的缺点——一旦一个类需要多个方法,构造函数就会变大;一旦下级类需要一个新的属性或方法,而不仅仅是修改接口,您还必须将其添加到链上的所有类构造函数中


我并不是说这是一种不好的方法——传递函数而不是类型可以清楚地说明您正在做什么,并且可以降低对象模型的复杂性。然而,在c#中,您的下一位开发人员可能会认为这很奇怪或令人困惑(取决于技能水平)。将一些面向对象方法和函数式方法混合在一起,至少可能会引起大多数与您一起工作的开发人员的注意。

这是一种有趣的方法。您可能需要注意两件事:

  • 就像Philip提到的,当你有很多方法要定义时,你最终会得到一个大的构造函数。这将导致类之间的深度耦合。一个以上或一个以下的代表将要求每个人修改签名。您应该考虑将它们作为公共属性并使用一些DI框架。

  • 将实现分解到方法级别有时可能过于细粒度。使用类/接口,您可以按域/功能对方法进行分组。如果将它们替换为代理,则它们可能会混淆,难以阅读/维护


  • 似乎代表的数量是一个重要的因素。

    我认为,模拟代表不需要任何模拟框架;您所要做的就是创建一个委托,该委托返回您在测试中需要它的内容。我没有在c语言中使用这些框架的经验,虽然它可能很容易使用,但我可以看到手动模拟委托的好处和方便性。模拟框架还有其他好处,那就是不必编写模拟-验证调用了方法,清楚(在代码中)测试代码和模拟设置之间的差异,我不同意关于混合OO和函数方法的评论。事实上,如果有更多的人能够在许多应用程序中分享他们对这两种方法的知识,那么就会有更多的代码被很好地抽象和解耦,并且在类、模块、名称空间等中更好地分配职责。这适用于各种语言。