实现一个接口,而不是提供一个接口 [英] Implementing an interface vs. providing an interface

查看:226
本文介绍了实现一个接口,而不是提供一个接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个同事,我已经提出了编码风格的冲突。我们都从一个界面开始说:

 界面IMessageList {
void AddMessage(IMessage message);
void RemoveMessage(IMessage message);
}

我将使用该界面的方式是我将一个类实现它:

  //方法1 
class SomeClass:IMessageList {
private IList< IMessage> messages = new List< IMessage>();
public AddMessage(IMessage message){
messages.Add(message);
}
public RemoveMessage(IMessage message){
messages.Remove(message);
}
}

然而,我的同事会做的是这样的:

  // Mehod 2 
界面IMessageListProvider {
IMessageList MessageList {get; }
}
class MessageList:IMessageList {
//类似于上述
的实现
class SomeClass:IMessageListProvider {
private IMessageList messageList = new MessageList中();
public MessageList {get {return messageList;他说他有这样的想法,从微软的代码来编程,但在我的研究中,我发现这听起来像提供者模式,它是一种微软特有形式的可插拔单身人士,但我的同事从来没有真正使用过它。这是他通常与界面一起工作的方式。此外,我发现一些人甚至在正确的上下文中对提供者模式至关重要: http://blog.ploeh.dk/2011/04/27/ProviderIsNotAPattern.aspx http://www.colourcoding.net/blog/archive/2009/07/29/microsofts -provider模式-A-坏主意携带出到perfection.aspx 。所以,放弃提供者模式本身的更具体的问题,这里是上面代码中看到的利弊:



关于第一种方法: / p>

优点:




  • 我们仅定义并使用一个合约( IMessageList ),我们使用 IMessageList 不依赖任何其他抽象。

  • 可扩展,因为可以容易地覆盖派生类型中的 IMessageList 成员,并通过 base.AddMessage 等访问基本实现。



缺点:




  • 实现很多接口的方式最终会有很多方法,名字冲突也会更容易出现。

  • 在许多类实现和转发接口方法的情况下,将与界面中的成员一样多的转发器。

  • IMessa之间没有封装geList 相关组件 SomeClass 和其他组件。



关于第二种方法:



优点:




  • 许多课程正在实施和转发 IMessageListProvider ,每个只需要覆盖一种方法。

  • IMessageList 保持在 SomeClass 之外,保持松动的耦合。



缺点:




  • 以这种方式实现很多提供者只是隐藏复杂性,而不是封装它。 li>
  • IMessageList 是我们定义的真正的合同,但几乎没有任何代码最终使用 IMessageList ,因为它总是要经过一个 IMessageListProvider 。我们的抽象依赖于另一个抽象。

  • 我们永远不能实现 IMessageList IMessageListProvider 在同一时间,从那以后,将有两种方法来获取IMessageList方法,一种可能是不正确的。这提供了对我们定义的类型的限制,但实现这两个接口不会导致任何编译器错误。

  • 我们不能轻易地覆盖 IMessageList 的行为,因为调用 base.MessageList 只会给我们一个对象。相反,我们必须重写它以返回基类使用的任何对象的子类,并扩展它。



在我的方法中,如果我们覆盖一个 IMessageList 成员,那么我们知道会发生什么,因为每个人都使用 IMessageList 。在他的方法中,我们需要为使用 IMessageListProvider 的类放置一个抽象工厂,以便我们可以将它们派生为实现我们想要的派生类型,但这似乎是很长的路要走,只是为了回到界面应该包含的简单多态。另外,似乎把我们锁定为只使用一个 IMessageList 的实现,因为它很难覆盖;但是,如果我们只有一个具体的实现,那么为什么大量的接口?我们从合同开始( IMessageList ),但现在我们依赖另一个抽象来使用它( IMessageListProvider ),这很难重写,它本质上限制我们使用一种类型( MessageList ),这实际上将我们的原始抽象变成了一个结论。

简而言之,我从他的方法中得到了不好的气味,但他坚持认为两者之间的选择只是一种风格的差异,所以我想知道我是否缺少某些东西。我做了很多的调查,看看我能否找到其他意见,但我只想出了提供者模式特定的东西,所以我不知道他们应用的好。所以我很好奇,社区对于提供一个界面作为一个属性获取器而不是实际实现接口的一般风格是什么。



作为一个妥协,我的同事我建议这样做:

  // Compromise 
class SomeClass:IMessageList {
private struct MessageList: IMessageList {
private IList< IMessage> messages = new List< IMessage>();
public AddMessage(IMessage message){
messages.Add(message);
}
public RemoveMessage(IMessage message){
messages.Remove(message);
}
}
private readonly messageList = new MessageList();
public AddMessage(IMessage message){
messageList.Add(message);
}
public RemoveMessage(IMessage message){
messageList.Remove(message);
}
}

这实现了他想要的 SomeClass 及其执行 IMessageList ,但不会以上述方式颠覆接口。或者 MessageList 可以进行内部或公开,以便其他类可以使用它。但是我相信 MessageList 应该总是转发到一个方便,并且永远不会简单地返回身体,因为那么实际上它成为接口的实现,这破坏了我们抽象。

解决方案

从我在这里看到的内容,我看不到需要 IMessageListProvider ,特别是如果您使用的依赖注入框架不吸。当提供的对象根据提供者类中的逻辑而变化时,具有 xxxProvider 的情况。以下是有效需要的原因IMessageListProvider


  1. 在运行时构造或获取 IMessageList 是昂贵的,其结构应该尽可能晚地推迟。

  2. 有不同的 IMessageList 的实现,必须在运行时通过 IMessageListProvider 实现中的逻辑来选择。

从您的描述中,这些都不是真的。这似乎是货物崇拜编程的明显示例,并强制消费者 IMessageListProvider 违反 Demeter法案


A coworker and I have come up with a coding style conflict. We both start off with an interface, say:

interface IMessageList {
    void AddMessage(IMessage message);
    void RemoveMessage(IMessage message);
}

The way I will use the interface is I will have a class implement it:

// Method 1
class SomeClass : IMessageList {
    private IList<IMessage> messages = new List<IMessage>();
    public AddMessage(IMessage message) {
        messages.Add(message);
    }
    public RemoveMessage(IMessage message) {
        messages.Remove(message);
    }
}

What my coworker will do, however, is this:

// Mehod 2
interface IMessageListProvider {
    IMessageList MessageList { get; }
}
class MessageList : IMessageList {
    // An implementation similar to the above
}
class SomeClass : IMessageListProvider {
    private IMessageList messageList = new MessageList();
    public MessageList { get { return messageList; } }
}

He says he got the idea to program this way from Microsoft code, but in my research I found that this sounds like the "provider" pattern which is "a Microsoft-specific form of pluggable singleton," but my coworker is never actually using it as such. This is just how he generally works with interfaces. Also, I've found some people who are critical of the provider pattern even in its proper context: http://blog.ploeh.dk/2011/04/27/ProviderIsNotAPattern.aspx, http://www.colourcoding.net/blog/archive/2009/07/29/microsofts-provider-pattern-a-bad-idea-carried-out-to-perfection.aspx. So, setting aside the more specific issues of the provider pattern itself, here are the pros and cons I see in just the code above:

As to the first method:

Pros:

  • We define and use only one contract (IMessageList), and our use of IMessageList is not dependent any other abstractions.
  • It is extensible because one can easily override the IMessageList members in a derived type and access the base implementations via base.AddMessage, etc.

Cons:

  • A class that implements a lot of interfaces this way will end up with a lot of methods, and name conflicts will more easily arise.
  • In cases where many classes are implementing and forwarding the interface methods, there will be as many forwarders as there are members in the interface.
  • There is no encapsulation between the IMessageList-related components of SomeClass and other components.

As to the second method:

Pros:

  • In cases where many classes are implementing and forwarding the IMessageListProvider, only one method needs to be overridden in each.
  • The implementation of the IMessageList is kept out of SomeClass, maintaining looser coupling.

Cons:

  • Implementing a lot of providers this way is merely hiding complexity, not encapsulating it.
  • IMessageList is the real contract we've defined, but hardly any code ends up using IMessageList alone because it always has to go through an IMessageListProvider. Our abstraction is dependent on another abstraction.
  • We can never implement IMessageList and IMessageListProvider at the same time, since then there would be two ways to get to the 'IMessageList' methods, and one could be incorrect. This puts forward restrictions on the types we're defining, but implementing both these interfaces will not cause any compiler errors.
  • We cannot easily override IMessageList's behavior since calling base.MessageList will only give us an object. Instead we have to override it to return a subclass of whatever object our base class is using and extend that.

In my method, if we override an IMessageList member, then we know exactly what will happen because everyone uses IMessageList. In his method, we need to put in an abstract factory for the classes that use IMessageListProvider so that we can send them our derived type that implements what we want, but this seems a long way to go just to get back to the simple polymorphism that an interface should entail. Also, it seems to lock us into using only one implementation of IMessageList since it's so hard to override; but, if we only ever have one concrete implementation, then why the swarm of interfaces? We started off with a contract (IMessageList), but now we're dependent on another abstraction to use it (IMessageListProvider), and this is so hard to override that it essentially restricts us to using one type (MessageList), which in effect turns our original abstraction into a concretion.

In short, I get a bad smell from his method, but he is adamant that the choice between the two is merely a style difference, so I wonder if I'm missing something. I did a lot of searching to see if I could find other opinions on this, but I only came up with things that were provider pattern-specific, so I don't know how well they apply. So I'm curious as to what the community thinks about this general style of providing an interface as a property getter versus actually implementing the interface.

As a compromise to my coworker I suggested something like this:

// Compromise
class SomeClass : IMessageList {
    private struct MessageList : IMessageList {
        private IList<IMessage> messages = new List<IMessage>();
        public AddMessage(IMessage message) {
            messages.Add(message);
        }
        public RemoveMessage(IMessage message) {
            messages.Remove(message);
        }
    }
    private readonly messageList = new MessageList();
    public AddMessage(IMessage message) {
        messageList.Add(message);
    }
    public RemoveMessage(IMessage message) {
        messageList.Remove(message);
    }
}

This achieves the encapsulation he wants between SomeClass and its implementation of IMessageList, but without subverting the interface in the manner above. Alternatively MessageList can be made internal or public so that other classes can use it. But it is my belief that MessageList should always be forwarded to as a convenience, and never simple returned bodily, because then in effect it becomes the implementation of the interface, which sabotages our abstraction.

解决方案

From what I'm seeing here, I don't see the need for IMessageListProvider, especially if you're using a dependency injection framework that doesn't suck. The case for having an xxxProvider comes when the "provided" object varies depending on the logic in the provider class. Here are valid reasons for needing IMessageListProvider:

  1. Constructing or obtaining an IMessageList at run-time is expensive, and its construction should be deferred as late as possible.
  2. There are different implementations of IMessageList that must be chosen at run-time by logic contained within the IMessageListProvider implementation.

From your description, neither of these appear to be true. This seems to be a clear example of cargo cult programming, and forces consumers of IMessageListProvider to violate the Law of Demeter.

这篇关于实现一个接口,而不是提供一个接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆