8

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

例子:

public interface ICut
{
    void Cut();
}
public class Knife : ICut
{
    void ICut.Cut()
    {
        //Cut Something
    }
}

并使用 Knife 对象:

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

你会推荐这种接口实现方法吗?为什么或者为什么不?

编辑:另外,鉴于我使用的是显式接口,以下内容将不起作用。

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

11 回答 11

4

引用 GoF 第 1 章:

  • “编程到接口,而不是实现”。
  • “优先于类继承的对象组合”。

由于 C# 没有多重继承,因此对象组合和接口编程是要走的路。

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

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

于 2008-10-07T15:02:00.097 回答
3

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

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 将消息写入它的消息日志,但客户端只能读取。这在您的类是 ComVisible 的情况下特别有用。您的客户端无法转换为 Writer 类型并更改消息日志中的信息。

于 2008-10-07T15:21:15.953 回答
2

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

public class Sword: ICut
{    
  void ICut.Cut()   
  {        
     //Cut Something   
  }
}

工厂可以退回一种锋利的工具!:

ICut obj = SharpImplementFactory();

obj.Cut();
于 2008-10-07T14:57:48.717 回答
2

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

于 2008-10-24T17:57:47.727 回答
2

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

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

于 2008-10-26T14:18:29.860 回答
1

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

这也有助于使继承更加简化,并为您提供更多使用多态的灵活性。

于 2008-10-07T14:54:57.333 回答
1

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

但是,如果您将基于接口的编程与控制反转和依赖注入相结合,那么您真的会有所收获。如果您从事测试驱动开发,您还可以通过这种类型的设置使用模拟对象进行单元测试。

然而,IOC、DI 和 TDD 本身都是主要主题,并且针对这些主题中的每一个都编写了整本书。希望这会给你一个可以研究的东西的起点。

于 2008-10-07T14:57:57.093 回答
1

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

考虑游戏中的一个逻辑实体,您决定代替负责绘制/勾选实体的类,您希望勾选/绘制代码在实体中。

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

较小的好处是在实现多个接口时,显式实现可以让您解决两个接口方法名称冲突的情况。

于 2008-10-24T17:43:33.123 回答
1

显式实现接口的另一个潜在场景是处理已经实现了功能但使用不同方法名称的现有类。例如,如果你的 Knife 类已经有一个名为 Slice 的方法,你可以这样实现接口:

public class Knife : ICut
{
     public void Slice()
     {
          // slice something
     }

     void ICut.Cut()
     {
          Slice();
     }
}
于 2009-06-12T16:57:53.750 回答
0

如果客户端代码除了可以使用对象之外不关心任何Cut()事情,那么使用ICut.

于 2008-10-07T14:54:35.820 回答
0

是的,但不一定出于给定的原因。

一个例子:

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

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

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

通过定义一组严格的交互方法(例如示例中的“剪切”方法),接口允许您获取一个对象(无论是业务对象、Web 控件还是您拥有的任何东西)并在定义的范围内使用它方式。

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

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

于 2008-10-07T14:57:41.900 回答