实现一个接口,而不是提供一个接口 [英] Implementing an interface vs. providing an interface
问题描述
界面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
:
- 在运行时构造或获取
IMessageList
是昂贵的,其结构应该尽可能晚地推迟。
- 有不同的
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
:
- Constructing or obtaining an
IMessageList
at run-time is expensive, and its construction should be deferred as late as possible.
- 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屋!