他の人がほのめかしているように、同じコードファイルを複数のプロジェクトに直接含めたくはありません。これは決して良い考えではなく、常に厄介なコードにつながります。2つの異なるプロジェクト間で共有する共通コードがある場合は、共通コードを含む3番目のプロジェクト(クラスライブラリ)を作成する必要があります。その後、他の2つのプロジェクトはクラスライブラリを参照するだけです。3つのプロジェクトすべてを同じソリューションに含めることができ、プロジェクト参照を使用できる場合に最適です。ただし、それができない場合でも、そのクラスライブラリプロジェクトによって出力されるDLLへの直接ファイル参照を持つことができます。
次に、スパゲッティコードを本当に避けたい場合は、依存性注入(DI)を真剣に検討する必要があります。私や他の人がこれを提案した理由は、共通のコードをクラスライブラリに移動して複数のプロジェクトで共有できるようにしても、クラスライブラリが「ブラックボックス」として機能するという問題が発生するためです。 「それはあなたのためにすべてを魔法のように理解し、あらゆる状況で適切に行動します。一見すると、それは開発者が努力すべき良いことのように聞こえますが、実際には、長期的には悪いコードにつながります。
たとえば、100の異なるプロジェクトで同じロギングクラスライブラリを使用し、それらすべてがわずかに異なる方法でロギングを行う必要がある場合はどうなりますか。これで、クラスライブラリは、これらのさまざまな状況をすべて魔法のように検出し、それらすべてを処理する必要があります。たとえば、プロジェクトによっては、ログを別のファイル名で保存する必要がある場合があります。ログをWindowsイベントログまたはデータベースに保存する必要がある場合があります。エラーがログに記録されたときに、通知を自動的に電子メールで送信する必要がある場合があります。などご想像のとおり、プロジェクトが増加し、要件が増大するにつれて、ロギングクラスライブラリはますます複雑で混乱する必要があり、必然的にバグが増えることになります。
一方、DIはこれらすべての問題を解決し、方法論に準拠している場合は、基本的に再利用可能なコードを作成する必要があります。簡単に言うと、クラスのすべての依存関係をクラスに注入(パラメーターで渡す)する必要があることを意味します。つまり、Loggerクラスにイベントログまたはデータベース接続が必要な場合は、それらを作成したり、連絡したりして、それら自体を見つけてはなりません。代わりに、それらの依存関係を(多くの場合コンストラクターで)渡す必要があります。DIを使用した例は、次のようになります。
Public Interface ILogFilePathFinder
Function GetPath() As String
End Interface
Public Class LogFilePathFinder
Implements ILogFilePathFinder
Public Sub New(isOemVersion As Boolean)
_isOemVersion = isOemVersion
End Sub
Private _isOemVersion As Boolean
Function GetPath() As String Implements ILogFilePathFinder.GetPath
If _isOemVersion Then
Return "C:\TestOem.log"
Else
Return "C:\Test.log"
End If
End Function
End Class
Public Interface ILogger
Sub WriteLog(ByVal text As String)
End Interface
Public Class FileLogger
Implements ILogger
Public Sub New(pathFinder As ILogFilePathFinder)
_pathFinder = pathFinder
End Sub
_pathFinder As ILogFilePathFinder
Public Sub WriteLog(text As String) Implements ILogger.WriteLog
Dim path As String = _pathFinder.GetPath()
' Write text to file ...
End Sub
End Class
ご覧のとおり、少し余分な作業が必要ですが、このようにコードを設計すると、後悔することはありません。ロガークラスが依存関係としてパスファインダーを要求していることに気付くでしょう。次に、パスファインダーは依存関係としてOEM設定を要求します。したがって、それを使用するには、次のようなことを行う必要があります。
Dim pathFinder As ILogFilePathFinder = New FileLogPathFinder(_OemSettings.IsOemVersion) ' Note, don't use a global to store the settings, always ask for them to be injected
Dim logger As ILogger = New FileLogger(pathFinder)
logger.WriteLog("test")
これで、どのような状況でもこのコードをすべて簡単に再利用できます。たとえば、異なるログファイルパスを使用する必要がある異なるプロジェクトがある場合でも、それらは共通FileLogger
クラスを使用できます。それぞれが独自のバージョンを実装してから、ILogFilePathFinder
そのカスタムパスファインダーを共通に挿入する必要がありFileLogger
ます。うまくいけば、この方法でそれを行うことが非常に便利で柔軟になり得ることがわかります。