C# 使用显式接口确保根据接口编程

C# 使用显式接口确保根据接口编程,c#,design-patterns,interface,C#,Design Patterns,Interface,我看到过使用显式接口作为将类使用锁定到该接口的方法的论点。其论点似乎是,通过强制其他人对接口进行编程,您可以确保更好地解耦类,并允许更轻松的测试 例如: public interface ICut { void Cut(); } public class Knife : ICut { void ICut.Cut() { //Cut Something } } 以及使用刀具对象: ICut obj = new Knife(); obj.Cut();

我看到过使用显式接口作为将类使用锁定到该接口的方法的论点。其论点似乎是,通过强制其他人对接口进行编程,您可以确保更好地解耦类,并允许更轻松的测试

例如:

public interface ICut
{
    void Cut();
}
public class Knife : ICut
{
    void ICut.Cut()
    {
        //Cut Something
    }
}
以及使用刀具对象:

ICut obj = new Knife();
obj.Cut();
您会推荐这种接口实现方法吗?为什么

编辑: 另外,考虑到我使用的是显式接口,下面的方法将不起作用

Knife obj = new Knife();
obj.Cut();

如果客户机代码只关心它可以使用对象来
Cut()
事物,那么使用
ICut

这是一个组织优势。您可以将ICuttingSurface、ICut和相关功能封装到一个自包含且可单元测试的程序集中。ICut接口的任何实现都很容易模仿,并且可以使其仅依赖于ICut接口,而不依赖于实际实现,这使得系统更加模块化和干净


此外,这有助于使继承更加简化,并使您能够更灵活地使用多基因疗法。

是的,但不一定是因为给定的原因

例如:

在我当前的项目中,我们正在构建一个数据输入工具。我们拥有所有(或几乎所有)选项卡都使用的某些功能,并且我们正在对单个页面(项目基于web)进行编码,以包含所有数据输入控件

此页面上有导航,以及与所有常用操作交互的按钮

通过定义一个接口(IDataEntry)来实现每个函数的方法,并在每个控件上实现该接口,我们可以在执行实际数据输入的用户控件上使用aspx页面激发公共方法

通过定义一组严格的交互方法(例如示例中的“cut”方法),接口允许您获取对象(可以是业务对象、web控件或您拥有的任何对象)并以定义的方式使用它

例如,您可以在任何ICut对象上调用cut,无论是刀、锯、喷灯还是单丝导线


出于测试目的,我认为接口也很好。如果根据接口的预期功能定义测试,则可以按照所述定义对象并对其进行测试。这是一个非常高级的测试,但它仍然确保了功能。但是,这不应取代单个对象方法的单元测试……如果“obj.Cut”导致切割错误的对象,或者在错误的位置切割,那么知道“obj.Cut”导致切割是没有好处的。

是的。不仅仅是为了测试。将常见行为考虑到接口(或抽象类)中是有意义的;这样就可以利用多态性

public class Sword: ICut
{    
  void ICut.Cut()   
  {        
     //Cut Something   
  }
}
工厂可能会退回一种锋利的工具!:

ICut obj = SharpImplementFactory();

obj.Cut();

在这种方法中使用接口本身不会导致代码解耦。如果这就是你所要做的,它只是增加了另一层混淆,可能会使这以后更加混乱

然而,如果您将基于接口的编程与控制反转和依赖项注入相结合,那么您就真的取得了进展。如果您喜欢测试驱动的开发,您还可以使用模拟对象对这种类型的设置进行单元测试


然而,IOC、DI和TDD本身都是主要的主题,所有这些主题的整本书都是针对这些主题编写的。希望这能给你一个可以研究的起点。

引用GoF第1章:

  • “程序到接口,而不是实现”
  • “支持对象组合而不是类继承”
由于C#没有多重继承,对象组合和接口编程是一条可行之路

埃塔:无论如何,你都不应该使用多重继承,但这完全是另一个话题


ETA2:我不太确定显式接口。这似乎没有建设性。为什么我想要一把刀,如果作为ICut实例化,它只能切割()?

我只在想限制访问某些方法的情况下使用过它

public interface IWriter
{
    void Write(string message);
}

public interface IReader
{
    string Read();
}

public class MessageLog : IReader, IWriter
{
      public string Read()
      {
           // Implementation

          return "";
      }

      void IWriter.Write(string message)
      {
           // Implementation
      }
}

public class Foo
{
    readonly MessageLog _messageLog;
    IWriter _messageWriter;

    public Foo()
    {
         _messageLog = new MessageLog();
         _messageWriter = _messageLog;
    }

    public IReader Messages
    {
        get { return _messageLog; }
    }
}

现在Foo可以使用_messageWriter将消息写入其消息日志,但客户端只能读取。这在类可见的场景中尤其有用。您的客户端无法强制转换为Writer类型并更改消息日志中的信息。

只允许期望显式接口类型的调用方确保方法仅在所需的上下文中可见

考虑游戏中的一个逻辑实体,然后你决定,你希望勾选/绘制的代码出现在实体中,而不是一个负责绘制/勾选实体的类

实现IDrawable.draw()和ITickable.tick()可确保实体只能在游戏需要时绘制/勾选。否则,这些方法将永远不可见


较少的好处是,当实现多个接口时,显式实现允许您解决两个接口方法名称冲突的情况。

这是一个坏主意,因为它们的使用破坏了多态性。使用的引用类型不应改变对象的行为。如果您想确保松散耦合,请将类设置为内部类,并使用DI技术(如Spring.Net)。

强制代码用户将对象强制转换为您希望他们使用的接口类型无疑具有某些优势


但是,总的来说,对接口进行编程是一个方法或过程问题。编程到一个接口并不仅仅是通过让你的代码让用户感到厌烦来实现的。

另一个显式实现接口的潜在场景是处理一个已经实现了该功能但使用了不同方法名的现有类。例如,如果您的刀具等级为a级
public class Knife : ICut
{
     public void Slice()
     {
          // slice something
     }

     void ICut.Cut()
     {
          Slice();
     }
}