グローバル変数の倒し方2
前回は
グローバル変数の読み取りだけしているメソッドについて、グローバル変数との依存を切る方法を紹介した。
今回は
グローバル変数への書き込みがあるメソッドについて。読み取りはあってもなくても構わない。
前回のコードの再掲になるが、今はこんな感じになっていた
Option Explicit On
Option Strict On
Option Infer On
Module Program
Dim ApplePrice As Integer = 0
Sub PrintApplePrice(applePrice As Integer)
Console.WriteLine("{0}円", applePrice)
End Sub
Sub UpdateApplePrice()
Console.Write("{0}円から、", ApplePrice)
ApplePrice = 80
Console.WriteLine("{0}円に更新", ApplePrice)
End Sub
Sub Main()
ApplePrice = 100
PrintApplePrice(ApplePrice)
UpdateApplePrice()
PrintApplePrice(ApplePrice)
End Sub
End Module
下から作業
グローバル変数の書き込み箇所が複数ある場合、処理の流れの下から作業するのが基本戦略となる。
処理の流れの上の方でヘタな変更をすると、処理の流れの下の方で思わぬ影響を受けることが多々ある。
極端な話、処理の最後の行でグローバル変数の値をどう変更しようがそれを参照することはないので安全。その安全な範囲を下の方から広げていくイメージ。
今回の例では `UpdateApplePrice` メソッドが対象となる。Main の `ApplePrice = 100` ではない。
コピーと書き戻し
グローバル変数への書き込みがあるメソッドをリファクタリングするには、ミラーコミットパターン "Mirror-Commit Pattern (MCP)" を使う。
グローバル変数をコピーし (Mirror)
メソッド内で変更し
グローバル変数に書き戻す (Commit)
具体的なコードを見た方が早いと思うので以下を見てほしい
Option Explicit On
Option Strict On
Option Infer On
Module Program
Dim ApplePrice As Integer = 0
Sub PrintApplePrice(applePrice As Integer)
Console.WriteLine("{0}円", applePrice)
End Sub
Sub UpdateApplePrice(ByRef price As Integer)
Console.Write("{0}円から、", price)
price = 80
Console.WriteLine("{0}円に更新", price)
End Sub
Sub Main()
ApplePrice = 100
PrintApplePrice(ApplePrice)
Dim price = ApplePrice
UpdateApplePrice(price)
ApplePrice = price
PrintApplePrice(ApplePrice)
End Sub
End Module
`UpdateApplePrice` の外では、ローカル変数にグローバル変数の値をコピーし、メソッド呼出し後には逆に書き戻す。
`UpdateApplePrice` 内では、今までグローバル変数の ApplePrice を使っていたところを、参照引数 (ByRef 引数)を使うように変更する。
またコードが長くなった。冗長だ!分かりづらい!という声が聞こえてくるような気もする。でも、やはり UpdateApplePrice と ApplePrice の依存関係が切れたことに注目してほしい。これもまた素晴らしい一歩。グローバル変数 ApplePrice は、Main メソッドとだけ依存している状況に改善された。
次回は
下からグローバル変数への書き込みを無くす戦略について追加で説明する。