VB.NET 类或模块的用途是什么? [英] VB.NET What is the purpose of a class or module?

查看:20
本文介绍了VB.NET 类或模块的用途是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Newbie sauce here... So, I tried to find the answer but couldn't.

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

What is the purpose of having a class or module? Everything I read tries to tell me what it is, but not what it's for. Why would I need to make one?

我读到的一切似乎都是对阅读教程的人做出假设,好像我知道很多一样.

Everything I read seems to makes assumptions about the person reading the tutorial, as if I know a lot.

推荐答案

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

A module is really very similar to just a class containing only shared members. In fact, in C#, there is no such construct as a "module". You cannot write any application without having at least one module or class, so I suspect your real question is not "why use classes and modules", but rather "why use multiple classes and modules and when is it appropriate to start a new one". Since modules and classes are essentially the same thing, I'll just focus on why you would have multiple classes at all. There are essentially four main reasons to create a new class:

  1. 将数据存储在谨慎的项目中
  2. 整理代码
  3. 在您的代码中提供接缝
  4. 将您的代码分成多个层并支持 n 层

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

Now, let's look at each one in more detail:

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

Often times you need to store multiple data about a single item and pass that data around between methods as a single object. For instance, if you write an application which works with a person, you will likely want to store multiple data about the person, such as their name, age, and title. You could obviously store these three data as three separate variables, and pass them as separate parameters to methods, such as:

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,如下所示:

However, it's often more convenient to pass all the data as a single object, for instance, you could create a MyPersonClass, like this:

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

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

And then you could pass all the data about a person in a single parameter, like this:

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

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

By doing it this way, it makes it much easier in the future to modify your person. For instance, if you needed to add the ability to store a skill for the person, and you had not put the person data in a class, you would have to go to every place in the code that passes person data and add the additional parameter. In a large project, it could be very difficult to find all of those places to fix, which could lead to bugs. However, the need for the class becomes even more apparent when you start needing to store a list of multiple people. For instance, if you need to store the data for 10 different people, you would need a list or array of variables, for instance:

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

当然,names(3)age(3) 都存储同一个人的数据,这一点并不明显.这是您必须知道的,或者您必须将其写在评论中,以免忘记.然而,当你有一个类来存储一个人的所有数据时,这更清晰、更容易:

It's of course, not at all obvious that names(3) and age(3) both store data for the same person. That is something you just have to know, or you have to write it in a comment so you don't forget. However, this is much cleaner and easier to do when you have the class to store all the data for a person:

Dim persons(9) As Person

现在,很明显persons(3).Namepersons(3).Age 都是同一个人的数据.通过这种方式,它是自我记录的.不需要评论来澄清你的逻辑.因此,代码将不再那么容易出错.

Now, it's completely obvious that persons(3).Name and persons(3).Age are both data for the same person. In that way, it is self-documenting. No comment is needed to clarify your logic. As a result, again, the code will be less bug-prone.

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

Often, classes will contain not only the data for a particular item, but also the methods that act on that data. This is a convenient mechanism. For instance, you may want to add a GetDesciption method to the person class, such as:

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

然后你可以这样使用它:

Then you can use it like this:

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

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

Which, as I'm sure you'll agree, is much cleaner and easier than doing something like this:

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

现在假设您要为每个人存储多个昵称.正如你很容易看到的那样,persons(3).Nicknames(0) 远比一些疯狂的二维数组简单,比如 nicknames(3)(0).如果您需要存储有关每个昵称的多个数据,会发生什么?如您所见,不使用类会很快变得混乱.

Now lets say you want to store multiple nicknames for each person. As you can easily see, persons(3).Nicknames(0) is far simpler than some crazy two dimensional array, such as nicknames(3)(0). And what happens if you need to store multiple data about each nickname? As you can see, not using classes would get messy very fast.

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

When you write a lengthy program, it can become very messy very quickly and lead to very buggy code if you do not properly organize your code. The most-important weapon you have in this battle against spaghetti-code is to create more classes. Ideally, each class will contain only the methods that are logically directly related to each other. Each new type of functionality should be broken out into a new well-named class. In a large project, these classes should be further organized into separate namespaces, but if you don't at least split them out into classes, you are really going to make a mess. For instance, lets say you have the following methods all thrown into the same module:

  • GetPersonDescription
  • GetProductDescription
  • FirePerson
  • 销售产品

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

I'm sure you'd agree, that it's just much easier to follow the code if these methods were broken out into separate classes, such as:

    • 获取描述
    • 获取描述
    • 销售

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

    And that's just a very, very simple example. When you have thousands of methods and variables dealing with many different items and different types of items, I'm sure you can easily imagine why classes are important to help organize and self-document the code.

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

    This one may be a bit more advanced, but it's very important, so I'll take a stab at trying to explain it in simple terms. Let's say you create a trace-logger class which writes log entries to a trace log file. For instance:

    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
    

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

    Now, lets say you want to have the logger class be able to write to a file or to a database. At this point it becomes obvious that writing the log entry to the file is really a separate type of logic which should have been in its own class all along, so you can break it out into a separate class, like this:

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

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

    Now, you can create a common interface and share it between two different classes. Both classes will handle writing log entries, but they will each perform the functionality in entirely different ways:

    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
    

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

    Now, that you have broken that data-access logic out into its own classes, you can refactor your logger class like this:

    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 类.这是可能的,因为您在条目格式化和条目编写之间的逻辑中创建了接缝.

    Now, you can reuse the TraceLogger class in many more situations without having to ever touch that class. For instance, you could give it an ILogWriter object that writes the entries to the windows event log, or to a spreadsheet, or even to an email--all without ever touching the original TraceLogger class. This is possible because you have created a seam in your logic between the formatting of the entries and the writing of the entries.

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

    The formatting doesn't care how the entries get logged. All it cares about is how to format the entries. When it needs to write and entry, it just asks a separate writer object to do that part of the work. How and what that writer actually does internally is irrelevant. Similarly, the writer doesn't care how the entry is formatted, it just expects that whatever is passed to it is an already-formatted valid entry that needs to be logged.

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

    As you may have noticed, not only is the TraceLogger now reusable to write to any kind of log, but also, the writers are reusable for writing any type of log to those types of logs. You could reuse the DatabaseLogWriter, for instance, to write both trace logs and exception logs.

    关于依赖注入的一点咆哮

    稍微逗我一下,因为我把这个答案加长了一点,并咆哮了一些对我来说很重要的事情......在最后一个例子中,我使用了一种称为依赖注入(DI)的技术.之所以称为依赖注入,是因为 writer 对象是 logger 类的依赖,并且该依赖对象通过构造函数注入到 logger 类中.通过执行以下操作,您可以在没有依赖项注入的情况下完成类似的操作:

    Just humor me, a little, as I make this answer a little bit longer with a rant about something important to me... In that last example, I used a technique called dependency injection (DI). It's called dependency injection because the writer object is a dependency of the logger class and that dependency object is injected into the logger class via the constructor. You could accomplish something similar without dependency injection by doing something like this:

    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
    

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

    However, as you can see, if you do it that way, now you'll need to modify that logger class every time you create a new type of writer. And then, just to create a logger, you have to have references every different type of writer. When you write code this way, pretty soon, any time you include one class, you suddenly have to reference the whole world just to do a simple task.

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

    Another alternative to the dependency injection approach would be to use inheritance to create multiple TraceLogger classes, one per type of writer:

    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
    

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

    Doing it with inheritance, like that, is better than the mode-enumeration approach, because you don't have to reference all the database logic just to log to a text file, but, in my opinion, dependency injection is cleaner and more flexible.

    回到逻辑接缝总结

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

    So, in summary, seams in your logic are important for reusability, flexibility, and interchangeability of your code. In small projects, these things are not of the utmost importance, but as projects grow, having clear seams can become critical.

    创建接缝的另一大好处是它使代码更加稳定和可测试.一旦您知道 TraceLogger 可以工作,就可以将其扩展以备将来使用,例如将日志写入电子表格,而无需接触实际的 TraceLogger代码>类.如果您不必接触它,那么您就不会冒引入新错误和可能危及已经使用它的其余代码的风险.此外,单独测试每一段代码变得更加容易.例如,如果您想测试 TraceLogger 类,您可以在测试中使用一个伪造的 writer 对象,该对象只记录到内存或控制台或其他东西.

    Another big benefit of creating seams is that it makes the code more stable and testable. Once you know that the TraceLogger works, there is a big advantage to being able to extend it for future uses, such as writing logs to a spreadsheet, without having to touch the actual TraceLogger class. If you don't have to touch it, then you don't risk introducing new bugs and potentially compromising the rest of the code that already uses it. Also, it becomes far easier to test each piece of your code in isolation. For instance, if you wanted to test the TraceLogger class, you could just, for your test, make it use a fake writer object which just logs to memory, or the console, or something.

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

    Once you have properly organized your code into separate classes, where each class is only responsible for one type of task, then you can start to group together your classes into layers. Layers are just a high-level organization of your code. There's nothing specific in the language that makes something technically a layer. Since there's nothing directly in the language that makes it clear where each layer starts and ends, people will often put all the classes for each layer into separate namespaces. So, for instance, you may have namespaces that look like this (where each namespace is a separate layer):

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

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

    Typically, you always want to have at least two layers in your code: the presentation or user-interface layer and the business-logic layer. If your application does any data access, that is typically put in its own layer as well. Each layer should be, as much as possible, independent and interchangeable. So, for instance, if our TraceLogger class in the above example is in a business layer, it should be reusable by any kind of UI.

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

    Layers expand upon all of the previous topics by providing further organization, self-documentation, reusability, and stability. However, another major benefit for layers is that it becomes far easier to split your application into multiple tiers. For instance, if you need to move your business and data access logic into a web service, it will be very simple to do so if you have already written your code cleanly into defined layers. If, however, all of that logic is intermingled and interdependent, then it will be a nightmare to try and break just the data access and business logic out into a separate project.

    简而言之,您永远需要创建多个类或模块.在单个类或模块中编写整个应用程序总是可能.毕竟,在面向对象的语言被发明之前,整个操作系统和软件套件就已经开发出来了.然而,面向对象编程 (OOP) 语言如此流行是有原因的.对于许多项目而言,面向对象非常有用.

    In short, you never need to create more than one class or module. It's always going to be possible to write your entire application in a single class or module. Entire operating systems and software suites were developed, after all, before object oriented languages were even invented. However, there is a reason why object-oriented programming (OOP) languages are so popular. For many projects, object-orientation is incredibly beneficial.

    这篇关于VB.NET 类或模块的用途是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆