昨日の記事では、「依存トレイン」の概念を紹介しました。これは、コードライブラリから関数をインポートするときに発生しますが、すべての依存関係を満たすためだけに、いくつかの追加モジュールをインポートする必要があります。必要なのは単一の「シート」(関数)だけだった場合、コードモジュールの「トレイン」全体が残ります。
モジュールが緊密に結合されている場合、この状況に陥ります。それで、あなたはそれについて何ができますか?この状況に対処する方法はいくつかあります。
より多くの「スタンドアロン」モジュールをもたらす緩い結合を維持する1つの方法は、呼び出し元モジュールのソースモジュールから関数のプライベートコピーを作成することです。これにより、2つのモジュール間の依存関係がなくなりますが、コードが繰り返されます。
コンセプトはシンプルです。プライマリコードモジュールから関数をコピーします。次に、それを呼び出し元のモジュールに貼り付けますが、あいまいさを避けるためにプライベートとしてマークします。
これは、次の状況で意味があります。
- 複雑なロジックのない単純なメソッド。
- 変更される可能性が低い手順。
- ルーチンがはるかに大きなモジュールの一部であり、モジュール内の他の関数が不要な場合。 1つの関数をコピーすると、肥大化を回避できます。
このアプローチには、次の欠点があります。
- ある場合 コピーした関数のバグ。多くの場所で修正する必要があります。
- とにかくソースモジュールをインポートすることになった場合、コードが重複します。
以前は、すべての標準コードライブラリモジュールを完全にスタンドアロンに保つために多大な労力を費やしていました。問題は、多くのコードの重複が発生することでした。その理由は、私が使用するために他のモジュールにコピーしていた関数のほとんどが、とにかくアプリケーションにインポートしていたモジュールからのものだったためです。
この典型的な例は、私の StringFunctionsでした。 モジュール。そのモジュールには、コードを読みやすくするために主に存在するいくつかの簡単なメソッドがあります。たとえば、Conc()
があります コードライブラリモジュールの半分以上にプライベート関数として含めていた関数。
時間が経つにつれて、私はその StringFunctionsを含めたことに気づきました 私のすべてのプロジェクトのモジュール。そのモジュールから関数を呼び出したとき、新しい依存関係を導入することはありませんでした。私は時間を無駄にし、ほとんどまたはまったく利益を得るために重複コードを導入していました。
すべてのアプリケーションに安全に想定できるコードモジュールがいくつかありました。それらは私が最も頻繁に使用した機能を備えたモジュールでした。つまり、これらの依存関係の多くは本質的に無視できるということです。
私は現在、最初にすべての新しいプロジェクトにインポートするコードモジュールの「標準ライブラリ」を維持しています。新しい依存関係を導入しないことを知って、これらのモジュールから関数を自由に呼び出すことができるようになりました。
私の「標準ライブラリ」のモジュールの1つは、クラスモジュール( clsApp )です。 )現在のユーザー名やタイトルバーのテキストなど、アプリケーションレベルのプロパティとメソッドが含まれます。 clsApp内から他のクラスモジュールも公開します 、 clsStatusなど およびclsRegistry 、アクセスステータスバーとWindowsレジストリへのより読みやすいアクセスをそれぞれ提供します。
ただし、すべてのプロジェクトでステータスバーやWindowsレジストリにアクセスする必要はありません。したがって、 clsStatusへの依存関係の作成を回避するため またはclsRegistry クラスの場合、一意の「コメントトークン」を使用してそれらのクラスを参照するコードをコメントアウトします。
これは、例を使って説明するのが最も簡単です:
' Notes
' - Find and replace '$$ with blank to enable Status property (requires clsStatus)
' - Find and replace '&& with blank to enable Reg property (requires clsRegistry)
'$$Private m_objStatus As clsStatus
'&&Private m_objReg As clsRegistry
'$$Public Property Get Status() As clsStatus
'$$ Set Status = m_objStatus
'$$End Property
'&&Public Property Get Reg() As clsRegistry
'&& Set Reg = m_objReg
'&&End Property
Private Sub Class_Initialize()
'$$ Set m_objStatus = New clsStatus
'&& Set m_objReg = New clsRegistry
End Sub
Status
を有効にしたい場合 上記のクラスのプロパティで、'$$
でグローバル検索と置換を実行できます 。
これはしばらくの間はうまくいきましたが、私にはいつも不器用でした。おそらくそうだったからでしょう。もう1つ注意すべき点は、コメントトークンは、コードライブラリ全体でグローバルに一意である必要があるということです。このアプローチを長く続けていたら、これはメンテナンスの悪夢だったでしょう。
よりクリーンなアプローチは、条件付きコンパイルを利用することです。これらは、ポンド/ハッシュタグ記号( "#")で始まるVBAの行です。その文字で始まる行は「前処理」の対象になります。
前処理とは何ですか?これは、プログラミング言語がコンパイルの前に行うステップです。したがって、コンパイル時のチェックが行われる前に、前処理行が評価されます。これにより、他の方法ではプロジェクトにコンパイルできないコードを配置できます。
コードライブラリでこれをどのように利用できますか?繰り返しになりますが、これは例を使って説明するのが最も簡単です:
' Notes
' - Replace the '$$ and '&& kludges with conditional compilation
#Const EnableStatusProperty = True 'If True, requires import of clsStatus class
#Const EnableRegProperty = False 'If True, requires import of clsRegistry class
#If EnableStatusProperty Then
Private m_objStatus As clsStatus
#End If
#If EnableRegProperty Then
Private m_objReg As clsRegistry
#End If
#If EnableStatusProperty Then
Public Property Get Status() As clsStatus
Set Status = m_objStatus
End Property
#End If
#If EnableRegProperty Then
Public Property Get Reg() As clsRegistry
Set Reg = m_objReg
End Property
#End If
Private Sub Class_Initialize()
#If EnableStatusProperty Then
Set m_objStatus = New clsStatus
#End If
#If EnableRegProperty Then
Set m_objReg = New clsRegistry
#End If
End Sub
ご覧のとおり、これは「依存関係トレイン」の問題を回避するための非常にクリーンな方法です。
オプションの依存関係を作成できます 。別のコードライブラリモジュールに依存するすべてのコードは、条件付きコンパイル#If...Thenステートメント内にラップされます。条件付きコンパイル定数はすべて、コードモジュールの上部にリストされています。
これで、コードライブラリモジュールの更新バージョンを再インポートするときに、条件付きコンパイルフラグを確認して、以前と同じように設定する必要があります。フラグがどのように設定されたかを覚えていない場合は、プロジェクトが完全にコンパイルされるまで、フラグのコンパイルと調整を続けることができるはずです。
また、バージョン管理を使用している場合は、以前にあったものを忘れることを心配する必要はありません。