この記事について
前回の続きでVB6.0を例に古いソースコードの仕様変更でSAN値を削らないための指針について考えたことをまとめて記載するよ。この記事は第2回、関数(Function, SubRoutine, Methodなど)について書いていくよ
SAN値を削る関数の傾向
それじゃあ実際に私が遭遇したSAN値を削りに来る関数をいくつか紹介しようと思う。
1. ステップ数が半端じゃなく多い
大体1,000ステップは余裕であるというのが普通なんじゃないだろうか?という感じでとにかく詰め込んでくる。しかも中味を見ると似たよーなことを条件分岐でやってることがほとんど。これをきちんと読みこなして「厳密にはこの範囲だとこの処理、そうじゃないこの範囲だとこの処理」というのを理解するまでに一苦労のパターンです。
2. Fast Failしてない
とにかくFor文とIf文のネストでしっちゃかめっちゃかにいじくった挙げ句、やりたかったのは特定の条件で処理を抜けたかったというパターン。もうね、過集中全開で読んでいった挙げ句「この処理の時だけ処理中止」なんてのが出てくるとがっくりくるわけですよ。
3. お前は一体何なんだ?的読んでもよく分からない命名の関数
これはこの後のフォームプログラム編でも取り上げようと思うのですが、とにかく命名がイケてない。変数編の何じゃこりゃ命名に通じるものがある。
いくつか例を挙げるなら
'====================
' 製品名称取得
'====================
Private Function F_GetHin() As Integer
' Do Something
F_GetHin = 1
End Function
Private Sub SM_Proc2()
' Do Some Bullshit Job
End Sub
とまぁ、上に至っては「Hinって何だよHin!!」とか、下はもはや「君ぃ~。何をどうしてプロシージャー2番なんて名前をつけたの😊❓ん❔」とクロムモリブデン鋼で出来たでっかいはてなマークでこめかみをガッつんガッつん殴り倒してやりたい感じの命名なんですね。
100歩譲って上はまだコメントがついてるから許そう。だが下、貴様はギルティー。
というわけでこんなのを読んでたらSAN値がいくつあっても足りないのでございますよ……。えぇ。
4. 同じ事を何度もやっとる……何も成長していない……
下のコードを見て「あー。書き直さないと不味いわ」と思わない者だけが今後も秘伝のソースを継ぎ足すと良い……
Private Function F_ChkKara() As Integer
Dim Cnt As Long
For Cnt = 0 to 155
If Cnt = 1 AND Txt(Cnt)<> "" Then
MsgBox "妥当ではありません", vbExclamation, "画面名"
Else Cnt = 2 And Txt(Cnt)<> "" Then
MsgBox "妥当ではありません", vbExclamation, "画面名"
Else Cnt = 3 And Txt(Cnt)<> "" Then
MsgBox "妥当ではありません", vbExclamation, "画面名"
'...
'<中略>
'...
Else Cnt = 155 And Txt(Cnt)<> "" Then
MsgBox "妥当ではありません", vbExclamation, "画面名"
End If
' 本当に155個続いてて
' しかも155がマジックナンバーでハードコーディングされとるのを見たときは
' ぶっ倒れたよ。わたしゃぁ
End Function
流石にもうちょっと複雑なことしてましたけれども、大意としてはこんな感じ。膝から落ちましたよ。ええ。要は空文字チェックがしたかっただけなんですね。しかもエラーメッセージを欄ごとに変えるでもなし。
5. 余計な改行が大量にある。
普通ソースコード書いていくに際しては「前後の行に不必要に改行を入れない」と教わると思うんですが、次のようなコードも中にはあるんですね。
Private Function F_Hoge() As Integer
On Error Goto ErrCatch_Hoge
Dim lCnt As Long
Dim lSql As String
Dim lRs As ADODB.Recordset
Dim lCn As ADODB.Connection
lCn = New ADODB.Connection
lRs = New ADODB.RecordSet
' 冗談じゃ無く5~6行何も無い領域がある。
lSql = ""
lSql = lSql & "SELECT * FROM ホゲ WHERE"
For lCnt = 0 To 10
If lCnt <> 0 Then
lSql = lSql & "Foo" & lCnt "='" & "var_" & lCnt & "'"
' ここにもよく分からない空白がある。
Else
lSql = lSql & "AND Foo" & lCnt "='" & "var_" & lCnt & "'"
End If
Next lCnt
lRs.Close
lCn.Close
lRs = Nothing
lCn = Nothing
' また現れる空白地帯
Exit Function
ErrCatch_Hoge:
MsgBox "エラー発生"
Exit Function
End Function
おわかりいただけだだろうか……。
長い。とにかく長いのである。何だこの空白はと思わず頭を抱えて呻ってしまうほどに「何も無いところが多すぎる」
こんなの読んでたら頭おかしくなるってばよ。
※どうやら「老眼対策」でこういう書き方をする方が居るらしい……。
SAN値を守るためにする対策
0. 準備
"Victory Loves Preparation"とはよく言ったもので、十全の準備無くして勝利はアリエナイのである。
というわけで、まずテキストエディターを用意します。
個人的にはNotepad++とVisual Studio Codeの二刀流ですが、とにかく改行を判定できて正規表現で置換が出来れば問題ありません。
1. 関数ごとに整形を実施
まずは無駄な改行をすべて削除します。
多分皆様おなじみの
^(\s+)?\n$
で先頭から空白があっても無くても良くて改行までの空白行を探して、置換で削除します。
2. ロジックの分割
とにかく長ったらしい上に同じような処理をしている部分については共通化を図ります。
例えば空文字チェックなんかは普通の言語でしたらIsNullOrEmpty関数などあるでしょうし、無い場合は
Private Function IsEmpty(aStr As String) As Boolean
On Error Goto ErrCache_IsEmpty
If aStr = "" Or Len(aStr) = 0 Then
IsEmpty = True
Else
IsEmpty = False
End If
Exit Function
ErrCache_IsEmpty:
Call MsgBox("Error Number: " Err.Number & vbNewLine & "IsEmptyでエラーが発生しました" & Err.Description & vbNewLine & Err.Source, vbCritical, "フォーム名")
End Function
とでもしておけばまぁ問題無いでしょう。
3. 条件の簡略化
みなさん、中学の数学でやったはずのド・モルガンの法則、覚えてますか?
あれ、使います。超使います。
Wikipediaの概要の部分に既に掲載されている概念コードのとおりですが
!(P || Q) == !P && !Q
If Not( P=True OR Q=True) <=> If P<>True AND Q<>True <=> If P=False AND Q=FALSE
!(P && Q) == !P || !Q
If Not( P=True And Q=True) <=> If P<>True OR Q<>True <=> IF P=False OR Q=False
といった具合に書き換えが出来るわけです。そしてクソコードには概して「P<>(特定の値)
」というのがANDとかORでいっぱいくっついたのが頻出するわけです。とすればこの書き換えを使って<>を取った形に書き換えるだけでも可読性も上がりますし、なにより計算コストが安くなります。(基本的に計算機は「○○でない」という否定の計算が苦手)
4. コードの隠ぺい
1~3まではコードを如何に見通し良くするかという話でしたが、こっからはずるいことをします。すなわち「動いている奴は良いコードだ。テストに合格した奴はよく訓練された良いコードだ」というわけで、今動いている関数に一切手をつけません。その代わり、ファイルの先頭または末尾にそういう関数を固めて「ここは見ないゾーン」を作ります。
これだけで心理的安全性がだいぶ保たれるようになります。
一方よく訓練された(ry ゾーンは蠱毒と化すのですがね……。
5. (※関数の使用箇所がすべて把握できるときに限って)関数名の変更
これは事前調査が結構物を言います。そんなときに使えるのがVisual Studio Codeくん。フォルダーを開いてその中を文字列検索してやれば部分一致も完全一致も正規表現もお手の物で関数名を列挙してくれますし、VB6.0でも同一ファイル中であれば確実に定義の参照やピークで定義を見たり出来るので、この機能を利用して関数名の順次置換を実施します。
Appendix: 関数名の命名規則について
基本的に関数、サブルーチン、プロシージャーなどの命名は英語の命令形("V+O"または"V+O+C")で記述するというのが一般的かと思います。そうは言っても「なんか取ってきてくっつけたいんだけれどもGetなのかPutなのかObtainなのかRegisterなのか何を使えば良いか分からんぞい」という時には、MicrosoftがPowerShell用ですが、プレフィクス一覧を作ってくれています。
ここに記載のある動詞+目的語で命名してやれば大体イカした命名になると思います。
おわりに
関数編はちょっとで済むかなぁと思ったんですが、そんなこと無かったよ……。
ここに書いてあることを参考にしていただいて少しでもSAN値を温存していただければと思います。
来週中にはフォームプログラミング編を作りたいなぁ……。