面向对象 - 在哪里放置此接口声明

发布于 2024-07-30 14:57:03 字数 666 浏览 6 评论 0原文

我有几个问题要问各位聪明人,涉及接口和抽象基类的面向对象设计。 考虑以下场景:

我有一个抽象类“DataObjectBase”和一个派生类“UserDataObject”。 我还有一个接口“IDataObject”。 当然,该接口公开了我的数据对象必须公开的所有公共方法和属性,并且您可能会猜测抽象基实现了所有数据对象通用的方法和属性。

我的问题是,如果抽象类 DataObjectBase 实现了接口 IDataObject 中指定的所有内容,那么该接口应该在基类上还是在派生类上声明?

在 C# 中,基类上声明的接口隐式应用于派生类,但这是最佳实践吗? 在我看来,在基类上实现接口使得派生类实现该接口变得不那么明显,但又需要为每个派生类指定接口。

此外,如果基类不是抽象的,推荐会改变吗?

第二个子问题:如果基类实现了 IDataObject 接口的所有方法/属性,是否还需要该接口? 基类类型名可以简单地用来代替接口名称,即:

private DataObjectBase _dataObject;
私有 IDataObject _dataObject;

在上面的示例中(基类再次实现了接口公开的所有内容),两者都可以分配相同的派生类型。 就我个人而言,我总是在这些情况下使用界面,但我有兴趣听到人们的想法。

提前致谢。

I have a few questions for you wise people involving OO design with Interfaces and abstract base classes. Consider the following scenario:

I have an abstract bass class "DataObjectBase" and a derived class "UserDataObject." I also have an interface "IDataObject." The interface of course exposes all of the public methods and properties that my Data Objects must expose, and you can probably guess that the abstract base implements the methods and properties common to all Data Objects.

My question is, if the abstract bass class DataObjectBase implements everything specified in the interface IDataObject, should the interface be declared on the base class, or on the derived classes(s)?

In C# interfaces declared on the base class are implicity applied to the derived classes, but is this the best practice? It seems to me that implementing the interface on the base class makes it less obvious that the derived class implements the interface, but then again requires the Interface to be specified for each derived class.

Additionally, if the base class was NOT abstract, would the reccomendation change?

A second sub-question: If the base class implements all of the methods/properties of the IDataObject interface, is the interface even needed? The base class typename can simply be used in place of the interface name, ie:

private DataObjectBase _dataObject;
private IDataObject _dataObject;

In the above example (where again the base implements everything exposed by the interface) both can be assigned the same derived types. Personally I always use the interface in these situations, but I am intrested in hearing peoples thoughts.

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

羞稚 2024-08-06 14:57:04

我思考此类问题的方式是考虑阅读代码的不同人,如果您愿意的话,可以考虑“角色”。 还要考虑系统整体的可维护性。

首先有一些代码期望使用该接口。 它是根据接口编写的,作者对实现没有(应该)感兴趣。 这就是我们提供 Interface 类的原因。 从这个角度来看,抽象基类只是许多可能的实现层次结构之一。 不要告诉这个角色实施细节。 保留接口。

然后我们的角色是设计一个实现。 他们提出了一种可能的方法并发现了一些变体,因此他们希望将通用代码整合在一起。 抽象基类 - 在这里填写常见的内容,让详细的实现者填补空白。 通过提供抽象方法来帮助他们,说“你的代码放在这里”。 请注意,这些方法不仅需要是接口中的方法。 另请注意,该抽象基类甚至可能实现多个接口! (例如,它是 CleverThingWorker,但也是 IntermediateWorkPersister。)

然后我们就有了实际执行精细实施的角色。 填写此处的空白。 死的很容易理解。 在这种情况下,您甚至不需要考虑接口本身。 你的工作就是使抽象类具体化。

底线...我使用接口和基类。 您将接口放在基类上。 我们不会通过将其添加到实现类来增加价值。

My way of thinking about such problems is to consider the different people reading the code, the "roles" if you like. Also consider the overall maintainability of the system.

First there is some code expecting to use the Interface. It's written in terms of the interface, the author has (should have) no interest in the implementation. That's why we provide the Interface class. From that perspective the Abstract Base Class is just one of many possible implementation hierarchies. Don't tell this role about implementation details. Keep the Interface.

Then we have the role who is designing an implementation. They come up with one possible approach and discover some variations, so they want to pull common code together. Abstract Base Class - fill in the common stuff here, let detailed implementers fill in the gaps. Help them by providing abstract methods saying "your code goes here". Note that these methods need not only be the ones in the Interface. Also note that this Abstract Base Class might even implement more that one Interface! (eg. It's CleverThingWorker but also a IntermediateWorkPersister.)

Then we have the role who actually do the fine detailed implementation. Fill in the gaps here. Dead easy to understand. In this case you don't even need to consider the Interface as such. Your job is to make that abstract class concrete.

Bottom line ... I use both Interfaces and Base classes. You put the Interface on the Base Class. We don't add value by adding it to the implementation class.

咆哮 2024-08-06 14:57:04

如果您的用户类始终继承自一个基类,那么您就不需要该接口。 如果您可能拥有与接口匹配但不是从基类派生的类,则使用该接口。

至于接口隐藏在基类中,因此在用户类中不能立即可见,这是正常的,可以由编译器处理。 这也是良好命名约定的用武之地 - UserDataObject 的名称与 IDataObject 匹配,DataObjectBase 也是如此。 您可以向类文件添加注释,说明它继承自 IDataObject,但可以看到它继承自 DataObjectBase,而 DataObjectBase 又看起来像是通过名称继承自 IDataObject。

If your user classes will always inherit from one base class, then you don't need the interface. If there is a possibility that you will have classes that match the interface but are not derived from the base class, then use the interface.

As for the interface being hidden in the base class and hence not immediately visible in the user class, this is normal and can be dealt withg by the compiler. This is also where good naming conventions come in - your UserDataObject has a name that matches IDataObject, as does DataObjectBase. You could add a comment to the class file that says it inherits from IDataObject, but it will be visible that it inherits from DataObjectBase, which in turn looks like it inherits from IDataObject by its name.

苦妄 2024-08-06 14:57:04

另外需要提到的是,使用接口可以更容易地实现自动化测试。

例如,假设接口的方法之一应该抛出异常 - 例如“DatabaseConnectionLostException” - 并且您想要测试客户端代码以检查它在这种情况下的行为是否正确。

提供抛出异常的接口的实现,允许编写测试是一件简单的事情。

如果你使用抽象基类而不是接口,这个操作会有点棘手(好吧,你可以使用Mocks,但是接口解决方案更干净)

The other thing that needs to be mentioned is that the use of interfaces makes it easier to implement automated tests.

Say, for example, that one of the methods of the interface is supposed to throw a exception - such as 'DatabaseConnectionLostException' - and you want to test client code to check that it behaves correctly in such a situation.

It is a simple matter to provide an implementation of the interface that throws the exception, allowing the test to be written.

If you used the abstract base class instead of the interface, this operation would be quite a bit trickier (OK, you can use Mocks, but the interface solution is much cleaner)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文