VB.NET 类或模块的目的是什么?
VB.NET 为什么需要类或模块的目的?
模块实际上与只包含共享成员的类非常相似。事实上,在C#中,没有“模块”这样的结构。没有类或模块,您无法编写任何应用程序,因此我怀疑您真正的问题不是“为什么使用类和模块”,而是“为什么使用多个类和模块以及何时适合启动一个新类或模块”。由于模块和类本质上是相同的,我将重点介绍为什么需要多个类。创建新类的主要原因有以下四个:
1. 将数据存储为离散项
2. 组织代码
3. 在代码中提供接缝
4. 将代码分割为层并支持多层架构
现在,让我们更详细地看看每个原因:
将数据存储为离散项
通常情况下,您需要存储有关单个项目的多个数据,并将这些数据作为单个对象在方法之间传递。例如,如果您编写一个与人员相关的应用程序,您可能想要存储关于该人员的多个数据,例如姓名、年龄和职称。您可以将这三个数据分别存储为三个单独的变量,并将它们作为单独的参数传递给方法,例如:
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
都是同一个人的数据,这是显而易见的。因此,它是自我说明的。不需要注释来澄清您的逻辑。结果,代码将更不容易出错。
通常,类不仅包含特定项目的数据,还包含对该数据进行操作的方法。这是一种方便的机制。例如,您可能想要为人员类添加一个GetDesciption
方法,例如:
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) ' 将时间戳追加到文本 ' 将文本写入文件 End Sub End Class
现在,假设您希望日志记录器类能够写入文件或数据库。此时,很明显,将日志条目写入文件实际上是一种单独的逻辑,本来应该在其自己的类中,以便您可以将其拆分为一个单独的类,如下所示:
Public Class TextFileLogWriter
Public Sub WriteEntry(text As String)
' 写入文件
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
' 写入文件
End Sub
End Class
Public Class DatabaseLogWriter
Implements ILogWriter
Public Sub WriteEntry(text As String) Implements ILogWriter.WriteEntry
' 写入数据库
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)
' 将时间戳追加到文本
_writer.WriteEntry(text)
End Sub
End Class
现在,您可以在不接触该类的情况下在许多其他情况下重复使用TraceLogger
类。例如,您可以给它一个写入条目到Windows事件日志、电子表格甚至电子邮件的ILogWriter
对象,而无需接触原始的TraceLogger
类。这是可能的,因为您已经在逻辑中创建了一个接缝,用于格式化条目和写入条目之间的工作。
格式化不关心条目的写入方式。它只关心如何格式化条目。当它需要写入条目时,它只会请求一个单独的写入器对象来执行该工作的部分。该写入器实际上如何执行内部逻辑是无关紧要的。同样,写入器不关心条目的格式化方式,它只期望传递给它的是已经格式化的有效条目,需要记录。
正如您可能已经注意到的,不仅TraceLogger
现在可重复使用以写入任何类型的日志,而且编写器也可重复使用以写入任何类型的日志。例如,您可以重用DatabaseLogWriter
来同时写入跟踪日志和异常日志。
关于依赖注入的小抱怨
请原谅我,稍微冗长一点,因为我要对一个对我来说很重要的问题进行抱怨...在上面的示例中,我使用了一种称为依赖注入(DI)的技术。它被称为依赖注入,因为写入器对象是日志记录器类的一个“依赖项”,并且该依赖项对象通过构造函数注入到日志记录器类中。您可以通过执行以下操作来实现类似的效果,而不使用依赖注入:
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) ' 将时间戳追加到文本 _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) ' 将时间戳追加到文本 _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
使用继承这样做比使用模式枚举方法好,因为您不必引用所有数据库逻辑只是为了将日志记录到文本文件中,但是在我看来,依赖注入更清晰、更灵活。
回到逻辑接缝的总结
因此,总之,您永远不需要创建多个类或模块。在单个类或模块中编写整个应用程序始终是可能的。毕竟,在面向对象的编程(OOP)语言甚至发明之前,已经开发了整个操作系统和软件套件。然而,面向对象的编程语言如此受欢迎是有原因的。对于许多项目来说,面向对象是非常有益的。
赶紧点!我没那么多时间 🙂
我对这个回复没有话可说。简直太棒了。您对VB.net语言的工作令人钦佩。
比我读过的大多数书都要好(实际上没有多少,但工作做得很棒),这些内容将被添加到我的个人知识库中。
文章结束了。
在编写软件时,代码的重用是一个非常重要的概念。为了实现代码的重用,可以使用模块和类。
模块是一个容器,可以将代码存储在其中,并在程序的其他部分引用它。通过使用模块,可以将相关的代码组织起来,使其更易于理解和维护。
类是面向对象编程中的一个更一般的概念,它允许您定义一个“事物”,然后在程序运行时创建多个该事物的实例。通过使用类,可以将相似的行为和属性封装到一个对象中,并通过创建该对象的多个实例来实现代码的重用。
假设您想在Visual Basic中创建一个虚拟宠物游戏。您允许用户添加任意数量的不同动物,并且您开始意识到跟踪这些动物非常复杂。通过使用类,这变得非常简单。
下面是一个示例代码,定义了一个名为PetDog的类:
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!”。
通过使用类,我们可以轻松地创建和管理多个相似的对象,从而实现代码的重用和灵活性。