ここに初心者ソース...それで、私は答えを見つけようとしましたが、見つかりませんでした。
クラスまたはモジュールを持つ目的は何ですか?私が読んだものはすべて、それが何であるかを教えてくれますが、それが何のためにあるのかは教えてくれません。なぜ私はそれを作る必要があるのですか?
私が読んだものはすべて、私がよく知っているかのように、チュートリアルを読んでいる人についての仮定をしているようです。
ここに初心者ソース...それで、私は答えを見つけようとしましたが、見つかりませんでした。
クラスまたはモジュールを持つ目的は何ですか?私が読んだものはすべて、それが何であるかを教えてくれますが、それが何のためにあるのかは教えてくれません。なぜ私はそれを作る必要があるのですか?
私が読んだものはすべて、私がよく知っているかのように、チュートリアルを読んでいる人についての仮定をしているようです。
モジュールは、共有メンバーのみを含むクラスに非常によく似ています。実際、C#には、「モジュール」などの構造はありません。少なくとも1つのモジュールまたはクラスがないとアプリケーションを作成できないので、本当の質問は「クラスとモジュールを使用する理由」ではなく、「複数のクラスとモジュールを使用する理由と、新しいクラスとモジュールを開始するのが適切な時期」だと思います。 。モジュールとクラスは基本的に同じものなので、複数のクラスがある理由に焦点を当てます。新しいクラスを作成する主な理由は、基本的に4つあります。
それでは、それぞれについて詳しく見ていきましょう。
多くの場合、単一のアイテムに関する複数のデータを格納し、そのデータを単一のオブジェクトとしてメソッド間で渡す必要があります。たとえば、人と連携するアプリケーションを作成する場合、名前、年齢、役職など、人に関する複数のデータを保存する必要があります。明らかに、これら3つのデータを3つの別個の変数として格納し、それらを別個のパラメーターとして次のようなメソッドに渡すことができます。
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
次に、次のように、人に関するすべてのデータを1つのパラメーターで渡すことができます。
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
メソッドをpersonクラスに追加したい場合があります。
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)
ように、などのクレイジーな2次元配列よりもはるかに単純です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
これで、共通のインターフェースを作成して、2つの異なるクラス間で共有できます。どちらのクラスもログエントリの書き込みを処理しますが、それぞれがまったく異なる方法で機能を実行します。
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
これが可能なのは、エントリのフォーマットとエントリの書き込みの間にロジックに 継ぎ目を作成したためです。
フォーマットは、エントリがどのようにログに記録されるかを気にしません。気になるのは、エントリのフォーマット方法だけです。書き込みと入力が必要な場合は、別のライターオブジェクトに作業のその部分を実行するように要求するだけです。そのライターが実際に内部でどのように何をするかは関係ありません。同様に、ライターはエントリがどのようにフォーマットされているかを気にせず、渡されたものはすべて、ログに記録する必要のあるフォーマット済みの有効なエントリであると想定します。
お気づきかもしれませんが、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)
' Append the time-stamp to the text
_writer.WriteEntry(text)
End Sub
End Class
ただし、ご覧のとおり、そのようにすると、新しいタイプのライターを作成するたびに、そのロガークラスを変更する必要があります。そして、ロガーを作成するには、さまざまなタイプのライターすべてを参照する必要があります。このようにコードを書くと、すぐに、1つのクラスを含めると、単純なタスクを実行するためだけに、突然全世界を参照する必要があります。
依存性注入アプローチのもう1つの代替方法は、継承を使用してTraceLogger
、ライターのタイプごとに1つずつ、複数のクラスを作成することです。
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
テキストファイルにログインするためだけにすべてのデータベースロジックを参照する必要がないため、このように継承を使用する方がモード列挙アプローチよりも優れていますが、私の意見では、依存性注入はよりクリーンで柔軟性があります。
ロジックシームの概要に戻る
したがって、要約すると、ロジックの継ぎ目は、コードの再利用性、柔軟性、および互換性にとって重要です。小規模なプロジェクトでは、これらのことは最も重要ではありませんが、プロジェクトが成長するにつれて、明確な継ぎ目を持つことが重要になる可能性があります。
シームを作成するもう1つの大きな利点は、コードがより安定してテスト可能になることです。動作することがわかったら、実際のクラスTraceLogger
に触れることなく、スプレッドシートにログを書き込むなど、将来の使用のために拡張できるという大きな利点があります。TraceLogger
それに触れる必要がない場合は、新しいバグを導入したり、すでにそれを使用している残りのコードを危険にさらしたりするリスクはありません。また、コードの各部分を個別にテストすることがはるかに簡単になります。たとえば、TraceLogger
クラスをテストしたい場合は、テストのために、メモリやコンソールなどにログを記録するだけの偽のライターオブジェクトを使用するようにすることができます。
コードを個別のクラスに適切に編成すると、各クラスは1つのタイプのタスクのみを担当し、クラスをレイヤーにグループ化することができます。レイヤーは、コードの高レベルの編成にすぎません。技術的に何かをレイヤーにする言語に固有のものは何もありません。各レイヤーの開始位置と終了位置を明確にする言語には直接何もないため、多くの場合、各レイヤーのすべてのクラスを別々の名前空間に配置します。したがって、たとえば、次のような名前空間がある場合があります(各名前空間は個別のレイヤーです)。
MyProduct.Presentation
MyProduct.Business
MyProduct.DataAccess
通常、コードには常に少なくとも2つのレイヤー(プレゼンテーションまたはユーザーインターフェイスレイヤーとビジネスロジックレイヤー)が必要です。アプリケーションがデータアクセスを行う場合、それは通常、独自のレイヤーにも配置されます。各レイヤーは、可能な限り独立していて交換可能である必要があります。したがって、たとえばTraceLogger
、上記の例のクラスがビジネスレイヤーにある場合は、あらゆる種類のUIで再利用できるはずです。
レイヤーは、さらなる編成、自己文書化、再利用性、および安定性を提供することにより、これまでのすべてのトピックを拡張します。ただし、レイヤーのもう1つの大きな利点は、アプリケーションを複数の層に分割することがはるかに簡単になることです。たとえば、ビジネスおよびデータアクセスロジックをWebサービスに移動する必要がある場合、コードを定義済みのレイヤーにクリーンに記述していれば、非常に簡単に移動できます。ただし、そのロジックがすべて混ざり合って相互依存している場合は、データアクセスとビジネスロジックだけを別のプロジェクトに分割しようとするのは悪夢です。
つまり、複数のクラスまたはモジュールを作成する必要はありません。アプリケーション全体を単一のクラスまたはモジュールで作成することは常に可能です。結局のところ、オブジェクト指向言語が発明される前に、オペレーティングシステムとソフトウェアスイート全体が開発されました。ただし、オブジェクト指向プログラミング(OOP)言語が非常に人気があるのには理由があります。多くのプロジェクトでは、オブジェクト指向は非常に有益です。
クラスは、状態(データ)と動作(メソッド)をカプセル化するメカニズムです。
コードに何らかの抽象化を加えたい場合は、クラスを使用する必要があります。これは、使用するためにコードを整理する方法です。
それらがないということは、コードがいたるところにあり、変更や保守が難しいものに縮退することを意味します。
コンピュータプログラムがとることができる最も基本的な形式は、プロシージャと呼ばれます。コンピュータが実行する命令(コード行)のリストを記述してから、プログラムを終了します。
ただし、多くのコンピュータプログラムは、必要になるたびに「実行」をクリックする個々のユーザーとは関係なく実行することを目的としています。ソフトウェアの作成に関するほとんどの議論の中心となるのは、コードを再利用するというこの概念です。モジュールを使用すると、コードをコンテナに格納し、プログラムの他の部分で参照できます。
クラスは、オブジェクト指向プログラミング全体のより一般的な概念であり、プログラムの実行中に「モノ」を定義し、それを複数回作成することができます。
VisualBasicでバーチャルペットゲームを作成したいとします。ユーザーが好きなだけ動物を追加できるようにすると、これを追跡するのは非常に複雑であることに気付き始めます。クラスを使用することで、これは本当に簡単になります...
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()
これで、コードで1回だけ定義しても、Dog1とDog2を互いに独立して操作できます。
Dog1.Name = "Fido"
Dog2.Breed = "Poodle"
Dog1.Bark()
上記のスニペットは「FidosaysWoof!」と出力します。
お役に立てば幸いです:)
カスタムタイプを作成し、後でそのタイプのオブジェクトを作成する場合は、クラスを使用します。
他の誰かのクラスを扱っていて、それらを強化するためにいくつかの迅速で汚い機能を実装する必要がある場合は、モジュールを使用してください。
C#のモジュールの代わりに共有クラスの共有メソッドを使用するため、最終的にはC#にモジュールがないため、C#開発者は今や酸っぱい顔をしている可能性があります。したがって、C#ではどちらの場合もクラスを使用します。そして、実際には、VB.NETでも同じことを行う必要があります。それはあなたに名前空間の分離の懸念を与えるので、あなたのインテリセンスはより組織化されます。モジュールを使用すると、すべてのモジュールのすべてのパブリックメソッドがメソッドのグローバルバケットに含まれます。
VB.NETのモジュールの唯一の良い使用法は、拡張メソッドを作成するときです。
クラス、フォーム、コード-「モジュール」はすべてVBのモジュールのタイプです。
モジュールは単にコードの名前付きコンテナであり、すべてのコードはある種のモジュールに含まれている必要があります。最も単純な方法は、ルーチンを意味のあるグループに編成する方法です。
最も基本的な、いわゆる「モジュール」(またはモジュールのより一般的な概念と区別するためにかつて呼び出された「コードモジュール」)は、それ以上のものを提供するだけでなく、外部に存在する変数を宣言する方法も提供します。単一のルーチンであり、ルーチンを終了しても消えることはありません。また、どのコード/モジュールがどのソースファイルにあるかを簡単に関連付けることができます。
クラスは、「インスタンス化」できるより強力なタイプのModule(*)です。つまり、クラスをデータ型のように扱い、異なる値/コンテンツで複数のコピーを作成し、変数に割り当て、渡すことができます。関数やその他のメソッドとの間でやり取りします。これらのコピーまたは「インスタンス」は、より一般的には「オブジェクト」と呼ばれます。
フォームは、視覚的なコンポーネントやコントロールと自動的に統合され、グラフィカルユーザーインターフェイスを作成するのに役立つ特殊なタイプのクラスです。
(*-今日、これは逆に教えられていることに注意してください。クラスは一般的な概念であり、VBコード-mdoulesは「シングルトン」と呼ばれる非常に限定された特殊なクラスです。クラスよりずっと前のモジュール。)
ある程度のスクリプトに精通しているようですが、必ずしもオブジェクト指向プログラミングである必要はありません。
クラスまたはモジュール(両方ともオブジェクト)を使用する理由は、関数がコードの再利用を可能にするのと同じように、オブジェクトが再利用を可能にするためです。
再利用性に加えて、データ(プロパティ)と関数の両方を含めることができるという利点があります。たとえば、Personクラスには、名前、生年月日などのプロパティと、年齢を計算するメソッドを含めることができます。
このように、新しい人がいるたびに、その人の新しいインスタンスを作成し、(誕生日と現在の日付に基づいて)年齢を簡単に計算できます。
または、年齢を計算する関数を記述し、各人の配列を作成してから、年齢配列を渡して年齢計算関数を呼び出すことで、これをすべて同じように行うことができますが、同じレベルの再利用性はありません。 。
有用な記事を見つけるために、オブジェクト指向プログラミングの紹介をグーグルで検索することができます。
簡単に言えば、プログラミングのクラスまたはモジュールは、スクリプトがロードされたときに多くのスクリプトにフォームを与えないために使用されます。したがって、それらをクラスとモジュールに減らす必要があります。