【问题标题】:Understanding the use of Interfaces and Base Classes了解接口和基类的使用
【发布时间】:2012-05-01 07:52:42
【问题描述】:

我知道有很多关于接口和基类的帖子,但我很难理解正确的设计模式。

如果我要编写一个报告类,我最初的想法是创建一个包含所有报告都将实现的核心属性、方法等的接口。

例如:

Public Interface IReportSales

Property Sales() As List(Of Sales)
Property ItemTotalSales() As Decimal

End Interface

Public Interface IReportProducts

Property Productss() As List(Of Inventory)
Property ProductsTotal() As Decimal

End Interface

然后我假设我会有一个类来实现接口:

Public Class MyReport
Implements IReportSales


Public Property Sales() As System.Collections.Generic.List(Of Decimal) Implements IReportItem.Sales
    Get
        Return Sales
    End Get
    Set(ByVal value As System.Collections.Generic.List(Of Decimal))
        Items = value
    End Set
End Property

Public Function ItemTotalSales() As Decimal Implements IReport.ItemTotalSales
    Dim total As Decimal = 0.0
    For Each item In Me.Sales
        total = total + item
    Next
End Function

End Class

我的想法是它应该是一个接口,因为其他报表可能不使用“项目”,这样我就可以实现用于给定报表类的对象。

我走了吗?我应该还刚刚创建了一个基类吗?我不创建基类的逻辑是,并非所有报表类都可以使用“项目”,所以我不想在不使用它们的地方定义它们。

【问题讨论】:

  • 你能解释一下这里实现接口的原因吗?你不觉得,这是不必要的记忆。我可以在没有界面的情况下做到这一点。对吗?
  • 你的问题让我有点困惑。您是否担心实施更多不使用Items 的报告?不过,您在界面中已将其定义为要求。就目前而言,您希望实现IReport 的任何类都必须提供Items() 属性和ItemTotalSales() 函数。
  • 仅仅因为你实现了一个包含Items的接口并不意味着你需要使用它。
  • @Guest:实现一个接口仅仅意味着你为它指定的任何 functionsproperties 提供一个实现。实现占用的空间量取决于实现,而不是接口。
  • 我想我的主要问题是这是使用界面的有效场景吗?

标签: asp.net vb.net interface base-class


【解决方案1】:

是的,如果你不知道有多少报表不会使用 Items ,你可以去 Abstract 类。

下面是另一个好主意:

你也可以同时创建接口和抽象类

在接口中定义销售,创建两个抽象类,一个用于实现两者的报表,另一个用于未实现销售的报表。实现两者的接口

首先定义两种方法(实施销售),然后仅实施销售。

为两个抽象类赋予适当的名称,例如ReportWithItemsBase 或 ReportWithoutItemsBase。

通过这种方式,您还可以在派生 Report 类时实现自我解释命名基类。

【讨论】:

  • 感谢您的额外想法。
【解决方案2】:

为了回答您的问题,抽象类用于为相关类提供一个共同的祖先。 .Net API 中的一个示例是 TextWriter。这个类为所有以某种方式编写文本的类提供了一个共同的祖先。

接口更适合用作不同对象的适配器,这些对象不属于同一个对象“家族”但具有相似的功能。 .Net API 中的各种集合就是一个很好的例子。

例如,ListDictionary 类为您提供了管理对象集合的能力。他们没有通过继承共享共同的祖先,这是没有意义的。不过,为了方便它们之间的互操作,它们实现了一些相同的接口。

这两个类都实现了IEnumerable。这完全允许您使用ListDictionary 类型的对象作为任何需要IEnumerable 的操作数。多么美妙!

因此,现在在您设计新软件的情况下,您需要考虑如何将其融入您的问题空间。如果你通过继承抽象类给这些类一个共同的祖先,你必须确保从它继承的所有项目都是真正的基类型。 (例如,StreamWriterTextWriter)。不恰当地使用类继承可能会使您的 API 在未来很难构建和修改。

假设您为您的报告创建了一个抽象类ReportBase。它可能包含一些非常通用的方法,所有报告都必须具备这些方法。也许它只是简单地指定了方法Run()

然后您只需要生成一种类型的报告,因此您定义了一个继承自 ReportBase 的具体 Report 类。一切都很棒。然后您发现您需要添加更多类型的报告,例如XReportYReportZReport。它们是什么并不重要,但它们的工作方式不同并且有不同的要求。所有的报告都会生成漂亮的 HTML 输出,每个人都很高兴。

下周您的客户说他们希望 XReportYReport 也能够输出 PDF 文档。现在有很多方法可以解决这个问题,但显然向抽象类添加OutputPdf 方法是一个糟糕的主意,因为其中一些报告不应该或不能支持这种行为!

现在这就是接口对您有用的地方。假设您定义了几个接口IHtmlReportIPdfReport。现在应该支持这些不同输出类型的报表类可以实现这些接口。然后,这将允许您创建一个函数,例如 CreatePdfReports(IEnumerable<IPdfReport> reports),它可以获取所有实现 IPdfReport 的报告,并对其执行所需的任何操作,而无需关心适当的基本类型是什么。

希望这会有所帮助,因为我不熟悉您要解决的问题,所以我有点从臀部射击。

【讨论】:

  • 这很棒,谢谢@Thomas。这是完全有道理的,我只是很难实施这个理论。感谢您的时间。
  • 如果您还不熟悉问题空间和您遇到的问题,我最大的建议是不要陷入尝试过度设计具有复杂类和接口结构的解决方案的困境重新将不得不解决。写你需要的东西,并愿意在你意识到它将变得有益时进行重构。
  • @TravisK 也不会因为遇到困难而感到难过。好的设计非常困难,几乎(或者可能是字面意思)第一次就做好。
  • 感谢您的鼓励。我已经学习了很多年,仍然试图弄湿我的脚。我想也许我应该跳进去尝试下一次机会。也许你的权利和我应该首先通过重构寻找实现的地方。
猜你喜欢
  • 1970-01-01
  • 2019-08-25
  • 1970-01-01
  • 2014-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多