使用显式接口确保针对接口进行编程

时间:2008-10-07 14:51:12

标签: c# design-patterns interface

我已经看到使用显式接口作为将类使用锁定到该接口的方法的参数。这个论点似乎是通过强迫其他人编程到接口,你可以确保更好地解耦类并允许更容易的测试。

示例:

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();

11 个答案:

答案 0 :(得分:4)

引用GoF第1章:

  • “编程到接口,而不是实现”。
  • “赞成对象组成超过类继承”。

由于C#没有多重继承,因此可以采用对象组合和接口编程。

ETA:你永远不应该使用多重继承,但这完全是另一个话题..: - )

ETA2:我对显式接口不太确定。这似乎没有建设性。为什么我想要一把只有Cut()的刀子如果作为ICut实现?

答案 1 :(得分: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类型并更改消息日志中的信息。

答案 2 :(得分:2)

是。而不仅仅是测试。将共同行为纳入接口(或抽象类)是有意义的;这样你可以利用多态性。

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

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

ICut obj = SharpImplementFactory();

obj.Cut();

答案 3 :(得分:2)

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

答案 4 :(得分:2)

毫无疑问,强迫代码用户将对象转换为您希望它们使用的接口类型具有一定的优势。

但是,总的来说,编程到接口是一种方法论或流程问题。仅通过使代码令用户烦恼,无法实现对接口的编程。

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

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

但是,如果将基于接口的编程与Inversion of Control和Dependency Injection相结合,那么你真的会到达某个地方。如果你进入测试驱动开发,你也可以使用Mock Objects进行单元测试。

然而,IOC,DI和TDD本身就是主要话题,并且已经在每个主题上写了整本书。希望这会给你一个你可以研究的事情的起点。

答案 7 :(得分:1)

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

考虑一个游戏中的逻辑实体,并且你决定在实体中绘制/勾选你想要滴答/抽取代码的实体而不是类责任。

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

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

答案 8 :(得分:1)

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

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

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

答案 9 :(得分:0)

如果客户端代码不关心除了可以将对象用于Cut()之外的事实,那么请使用ICut

答案 10 :(得分:0)

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

一个例子:

在我目前的项目中,我们正在构建一个数据输入工具。我们有一些所有(或几乎所有)选项卡使用的函数,我们编写单个页面(项目是基于Web的)来包含所有数据输入控件。

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

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

通过定义一组严格的交互方法(例如示例中的'cut'方法)接口允许您获取一个对象(无论是业务对象,Web控件还是您拥有的对象)并使用它以明确的方式。

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

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