7

新手酱在这里...所以,我试图找到答案但找不到。

拥有一个类或模块的目的是什么?我阅读的所有内容都试图告诉我它是什么,而不是它的用途。为什么我需要做一个?

我阅读的所有内容似乎都对阅读教程的人做出了假设,好像我知道很多。

4

7 回答 7

28

一个模块非常类似于只包含共享成员的类。事实上,在 C# 中,没有“模块”这样的结构。如果没有至少一个模块或类,您就无法编写任何应用程序,所以我怀疑您真正的问题不是“为什么使用类和模块”,而是“为什么使用多个类和模块以及何时开始一个新的” . 由于模块和类本质上是相同的,我将只关注为什么你会有多个类。创建一个新类基本上有四个主要原因:

  1. 将数据存储在谨慎的项目中
  2. 组织你的代码
  3. 在代码中提供接缝
  4. 将您的代码划分为层并支持 n 层

现在,让我们更详细地看一下每一个:

将数据存储在谨慎的项目中

通常,您需要存储有关单个项目的多个数据,并将该数据作为单个对象在方法之间传递。例如,如果您编写一个与某人一起工作的应用程序,您可能希望存储有关此人的多个数据,例如他们的姓名、年龄和头衔。您显然可以将这三个数据存储为三个单独的变量,并将它们作为单独的参数传递给方法,例如:

Public Sub DisplayPerson(name As String, age As Integer, title As String)
    Label1.Text = name
    Label2.Text = age.ToString()
    Label3.Text = title
End Sub

但是,将所有数据作为单个对象传递通常更方便,例如,您可以创建一个MyPersonClass,如下所示:

Public Class MyPersonClass
   Public Name As String
   Public Age As Integer
   Public Title As String
End Class

然后你可以在一个参数中传递一个人的所有数据,像这样:

Public Sub DisplayPerson(person As MyPersonClass)
    Label1.Text = person.Name
    Label2.Text = person.Age.ToString()
    Label3.Text = person.Title
End Sub

通过这种方式,将来修改您的人会变得更加容易。例如,如果您需要为人员添加存储技能的功能,并且您没有将人员数据放在类中,则您必须前往代码中传递人员数据的每个地方并添加附加参数. 在一个大型项目中,可能很难找到所有这些地方来修复,这可能会导致错误。但是,当您开始需要存储多人列表时,对类的需求变得更加明显。例如,如果您需要为 10 个不同的人存储数据,则需要一个变量列表或数组,例如:

Dim names(9) As String
Dim ages(9) As Integer
Dim titles(9) As String

当然,一点也不明显,names(3)两者age(3)都为同一个人存储数据。这是您必须知道的,或者您必须将其写在评论中,这样您就不会忘记。然而,当你有一个类来存储一个人的所有数据时,这会更干净、更容易:

Dim persons(9) As Person

现在,很明显,persons(3).Name两者persons(3).Age都是同一个人的数据。这样,它是自我记录的。不需要评论来澄清你的逻辑。结果,再次,代码将不太容易出错。

通常,类不仅包含特定项目的数据,还包含作用于该数据的方法。这是一种方便的机制。例如,您可能希望向GetDesciptionperson 类添加一个方法,例如:

Public Class MyPersonClass
   Public Name As String
   Public Age As Integer
   Public Title As String

   Public Function GetDescription() As String
       Return Title & " " & Name
   End Function
End Class

然后你可以像这样使用它:

For Each person As MyPersonClass In persons
    MessageBox.Show("Hello " & person.GetDescription())
Next

我相信你会同意,这比做这样的事情更干净、更容易:

For i As Integer = 0 To 9
    MessageBox.Show("Hello " & GetPersonDescription(title(i), names(i)))
Next

现在假设您要为每个人存储多个昵称。如您所见,persons(3).Nicknames(0)它远比一些疯狂的二维数组简单得多,例如nicknames(3)(0). 如果您需要存储有关每个昵称的多个数据会怎样?如您所见,不使用类会很快变得一团糟。

组织你的代码

当您编写一个冗长的程序时,如果您没有正确组织代码,它很快就会变得非常混乱并导致代码非常错误。在与意大利面条代码的战斗中,您拥有的最重要的武器是创建更多类。理想情况下,每个类将仅包含逻辑上彼此直接相关的方法。每种新类型的功能都应该分解为一个新的命名类。在一个大型项目中,这些类应该被进一步组织成单独的命名空间,但如果你至少不将它们分成类,那你真的会搞得一团糟。例如,假设您将以下方法全部放入同一个模块中:

  • GetPersonDescription
  • GetProductDescription
  • FirePerson
  • SellProduct

我相信您会同意,如果将这些方法分解为单独的类,则遵循代码会容易得多,例如:

  • Person
    • GetDescription
    • Fire
  • Product
    • GetDescription
    • Sell

这只是一个非常非常简单的例子。当你有成千上万的方法和变量处理许多不同的项目和不同类型的项目时,我相信你可以很容易地想象为什么类对于帮助组织和自我记录代码很重要。

在代码中提供接缝

这个可能更高级一些,但它非常重要,所以我将尝试用简单的术语来解释它。假设您创建了一个将日志条目写入跟踪日志文件的跟踪记录器类。例如:

Public Class TraceLogger
    Public Sub LogEntry(text As String)
        ' Append the time-stamp to the text
        ' Write the text to the file
    End Sub
End Class

现在,假设您希望记录器类能够写入文件或数据库。此时很明显,将日志条目写入文件实际上是一种单独的逻辑类型,应该一直在其自己的类中,因此您可以将其分解为单独的类,如下所示:

Public Class TextFileLogWriter
    Public Sub WriteEntry(text As String)
        ' Write to file
    End Sub
End Class

现在,您可以创建一个通用接口并在两个不同的类之间共享它。这两个类都将处理写入日志条目,但它们各自将以完全不同的方式执行功能:

Public Interface ILogWriter
    Sub WriteEntry(text As String)
End Interface

Public Class TextFileLogWriter
    Implements ILogWriter

    Public Sub WriteEntry(text As String) Implements ILogWriter.WriteEntry
        ' Write to file
    End Sub
End Class

Public Class DatabaseLogWriter
    Implements ILogWriter

    Public Sub WriteEntry(text As String) Implements ILogWriter.WriteEntry
        ' Write to database
    End Sub
End Class

现在,您已经将该数据访问逻辑分解为它自己的类,您可以像这样重构您的记录器类:

Public Class TraceLogger
    Public Sub New(writer As ILogWriter)
        _writer = writer
    End Sub

    Private _writer As ILogWriter

    Public Sub LogEntry(text As String)
        ' Append the time-stamp to the text
        _writer.WriteEntry(text)
    End Sub
End Class

现在,您可以在更多情况下重用TraceLogger该类,而无需接触该类。例如,您可以给它一个ILogWriter将条目写入 Windows 事件日志、电子表格甚至电子邮件的对象——所有这些都无需接触原始TraceLogger类。这是可能的,因为您已经在条目的格式和条目的写入之间创建了逻辑 接缝。

格式并不关心如何记录条目。它只关心如何格式化条目。当它需要写入和输入时,它只需要一个单独的 writer 对象来完成这部分工作。该作家在内部实际上如何以及做什么是无关紧要的。同样,作者并不关心条目的格式,它只希望传递给它的任何内容都是需要记录的已经格式化的有效条目。

您可能已经注意到,现在不仅可以TraceLogger重用写入任何类型的日志,而且写入器可重用用于将任何类型的日志写入这些类型的日志。例如,您可以重复使用DatabaseLogWriter来编写跟踪日志和异常日志。

关于依赖注入的一点抱怨

稍微幽默一下,因为我对一些对我很重要的事情进行了咆哮,使这个答案变得更长一点......在最后一个示例中,我使用了一种称为依赖注入(DI)的技术。之所以称为依赖注入,是因为 writer 对象是 logger 类的依赖项,并且该依赖项对象通过构造函数注入到 logger 类中。您可以通过执行以下操作来完成类似的操作,而无需依赖注入:

Public Class TraceLogger
    Public Sub New(mode As LoggerModeEnum)
        If mode = LoggerModeEnum.TextFile Then
            _writer = New TextFileLogWriter() 
        Else
            _writer = New DatabaseLogWriter() 
        End If
    End Sub

    Private _writer As ILogWriter

    Public Sub LogEntry(text As String)
        ' Append the time-stamp to the text
        _writer.WriteEntry(text)
    End Sub
End Class

但是,如您所见,如果您这样做,现在您需要在每次创建新类型的编写器时修改该记录器类。然后,仅仅为了创建一个记录器,你必须引用每种不同类型的编写器。当您以这种方式编写代码时,很快,每当您包含一个类时,您突然不得不引用整个世界来完成一项简单的任务。

依赖注入方法的另一种替代方法是使用继承来创建多个TraceLogger类,每种类型的编写器一个:

Public MustInherit Class TraceLogger
    Public Sub New()
        _writer = NewLogWriter() 
    End Sub

    Private _writer As ILogWriter

    Protected MustOverride Sub NewLogWriter()

    Public Sub LogEntry(text As String)
        ' Append the time-stamp to the text
        _writer.WriteEntry(text)
    End Sub
End Class

Public Class TextFileTraceLogger
    Inherits TraceLogger

    Protected Overrides Sub NewLogWriter()
        _Return New TextFileLogWriter() 
    End Sub
End Class

Public Class DatabaseTraceLogger
    Inherits TraceLogger

    Protected Overrides Sub NewLogWriter()
        _Return New DatabaseLogWriter() 
    End Sub
End Class

像这样使用继承比模式枚举方法更好,因为您不必引用所有数据库逻辑来记录到文本文件,但是,在我看来,依赖注入更干净、更灵活.

回到逻辑接缝总结

因此,总而言之,逻辑中的接缝对于代码的可重用性、灵活性和可互换性很重要。在小型项目中,这些事情并不是最重要的,但随着项目的发展,清晰的接缝可能变得至关重要。

创建接缝的另一大好处是它使代码更加稳定和可测试。一旦你知道它的TraceLogger工作原理,能够扩展它以供将来使用,例如将日志写入电子表格,而无需接触实际的TraceLogger类,这是一个很大的优势。如果您不必接触它,那么您就不会冒险引入新的错误并可能损害已经使用它的其余代码。此外,单独测试每段代码变得容易得多。例如,如果你想测试这个TraceLogger类,你可以为了你的测试,让它使用一个伪造的 writer 对象,它只记录到内存、控制台或其他东西。

将您的代码划分为层并支持 N 层

一旦你将你的代码正确地组织成单独的类,每个类只负责一种类型的任务,那么你就可以开始将你的类组合成。层只是代码的高级组织。语言中没有什么特定的东西可以在技术上使某些东西成为一个层。由于语言中没有直接说明每一层的开始和结束位置,因此人们通常会将每一层的所有类放入单独的名称空间中。因此,例如,您可能有如下所示的命名空间(其中每个命名空间都是一个单独的层):

  • MyProduct.Presentation
  • MyProduct.Business
  • MyProduct.DataAccess

通常,您总是希望代码中至少有两层:表示或用户界面层和业务逻辑层。如果您的应用程序进行任何数据访问,则通常也将其放在它自己的层中。每一层都应该尽可能地独立和可互换。因此,例如,如果TraceLogger上面示例中的类位于业务层中,则它应该可以被任何类型的 UI 重用。

层通过提供进一步的组织、自文档、可重用性和稳定性来扩展所有先前的主题。但是,层的另一个主要好处是将应用程序拆分为多个层变得容易得多。例如,如果您需要将业务和数据访问逻辑移动到 Web 服务中,如果您已经将代码干净地写入定义的层,那么这样做将非常简单。但是,如果所有这些逻辑都是混合和相互依赖的,那么尝试将数据访问和业务逻辑分解到一个单独的项目中将是一场噩梦。

我要说的结束

简而言之,您永远不需要创建多个类或模块。总是可以单个类或模块中编写整个应用程序。毕竟,甚至在面向对象语言发明之前,就已经开发了整个操作系统和软件套件。但是,面向对象编程 (OOP) 语言如此受欢迎是有原因的。对于许多项目,面向对象是非常有益的。

于 2012-12-29T22:38:44.043 回答
3

类是封装状态(数据)和行为(方法)的机制。

如果你想在你的代码中有任何类型的抽象,你需要使用类——组织你的代码以供使用的方法。

没有它们意味着你的代码到处都是,它会退化成难以改变和维护的东西。

于 2012-12-29T20:54:16.660 回答
3

计算机程序可以采用的最基本形式称为过程:您编写指令列表(代码行)供计算机执行,然后程序退出。

然而,许多计算机程序旨在独立于每次需要时单击“运行”来运行。重用代码的概念是大多数关于编写软件的讨论的核心。模块允许您将代码存储在容器中并在程序的其他部分中引用它。

类是面向对象编程中更通用概念,它允许您定义一个“事物”,然后在程序运行时多次创建它。

假设您想在 Visual Basic 中创建一个虚拟宠物游戏。您允许用户根据需要添加任意数量的不同动物,并且您开始意识到跟踪这一点非常复杂。通过使用类,这变得非常容易......

Class PetDog
   Private _id As Integer
   Public Name As String
   Public Breed As String

   Public Sub Bark()
      Console.WriteLine(Name + " says Woof!")
   End Sub

End Class

一旦您编写了此代码,允许用户将另一只狗添加到他们的宠物动物园就变得如此简单:

Dim Dog1 As New PetDog()
Dim Dog2 As New PetDog()

现在,您可以彼此独立地与 Dog1 和 Dog2 交互,尽管在您的代码中只定义了一次。

Dog1.Name = "Fido"
Dog2.Breed = "Poodle"
Dog1.Bark()

上面的代码片段会打印出“Fido says Woof!”。

希望有帮助:)

于 2012-12-29T22:11:47.443 回答
1

类、表单和代码——“模块”是 VB 中的所有模块类型。

模块只是代码的命名容器,所有代码都必须在某种模块中。在最简单的情况下,它们是将您的日常活动组织成有意义的组的一种方式。

最基本的,所谓的“模块”(或“代码模块”,因为它们曾经被称为将它们与更一般的模块概念区分开来)提供的只是这些,加上一种声明存在于任何外部的变量的方法单个例程并且在您退出例程时不会消失,并且是关联哪些代码/模块在哪些源文件中的一种简单方法。

是一种更强大的 Module(*) 类型,可以“实例化”,这意味着您可以将它们视为数据类型,创建具有不同值/内容的它们的多个副本,将它们分配给变量,传递它们往返函数和其他方法等。这些副本或“实例”通常称为“对象”。

表单只是一种特殊类型的类,它自动神奇地与可视组件和控件集成,以帮助您制作图形用户界面。

(* -- 请注意,今天这是相反的方式:类是一般概念,VB 代码模块只是一个非常有限的专业类,称为“单例”。但无论是在历史上还是在 Basic 的传统中,它都是模块早于类。)

于 2012-12-29T21:40:58.847 回答
1

如果您要创建自定义类型,然后创建该类型的对象 - 使用类。

如果您正在处理其他人的类 - 并且需要实现一些快速而肮脏的功能来增强它们 - 使用模块。

C# 开发人员现在可能会脸色难看,因为您将使用共享类的共享方法而不是 C# 中的模块,最终因为 C# 没有模块。因此,您将在 C# 中的两种情况下都使用类。你应该在 VB.NET 中做同样的事情,真的。它为您提供命名空间分离问题​​,因此您的智能感知更有条理。使用模块,所有模块中的所有公共方法在全局方法桶中 - 需要处理更多混乱。

VB.NET 中模块的唯一用途是编写扩展方法。

于 2012-12-29T21:16:28.717 回答
0

听起来您熟悉某种级别的脚本,但不一定熟悉面向对象的编程。

使用类或模块(它们都是对象)的原因很像函数允许重用代码,对象允许重用。

除了可重用性之外,它们还具有可以包含数据(属性)和函数的优点。例如,Person 类可以具有姓名、生日等属性,以及计算年龄的方法。

这样,每次您有一个新人时,您都会创建该人的一个新实例,并且很容易计算他们的年龄(基于他们的生日和当前日期)。

或者,您可以通过编写一个计算年龄的函数并为每个人创建一个数组,然后通过传递年龄数组来调用您的计算年龄函数来类似地执行所有这些操作,但是您不会具有相同级别的可重用性.

要找到有用的文章,您可以在 Google 上搜索面向对象编程的介绍。

于 2013-01-12T00:45:36.267 回答
0

简而言之,对我来说编程中的 Class 或 Module 用于在加载时不给太多脚本提供形式。所以我们需要将它们简化为类和模块。

于 2016-08-29T03:14:05.557 回答