読者です 読者をやめる 読者になる 読者になる

コードの見直し(リファクタリング?)

機能追加に伴ってコードがゴチャゴチャしてきた。見づらいだけならいいが、さらなる機能追加が容易でなくなってきて、やばい。機能追加が容易なように改良する必要がある。

気象庁コーディングルール(ルビ:バイブル)」を再度見ている。

変数参照型モジュール内の共有変数の値の設定・変更は、同じモジュール内の サブルーチンでしか行ってはいけない。外部からは値を参照するのみにする。

http://www.mri-jma.go.jp/Project/mrinpd/coderule.html

「外部とのやりとりはなし。外部に許可するのは参照 (read only) のみ」。しかし read only てのはグローバル変数のプロパティとしてはないので(引数なら intent(in) があるが)、「運用でなんとかする」つまり「たくさんあるグローバル変数を、ひとつも、誰も、外部で変えないことを祈る」ってことだよね。うーむ、危ないような。

と思ったけどよく考えたら自分も似たようなことしてたわ。ただ、自分の場合は下のような「パッケージ型モジュール」の共有変数をモジュール外で read only 許可している。なのでコードが見事にスパゲティ…。

パッケージ型モジュール内の共有変数や局所サブルーチンにはprivate 属性を つけ、モジュール内でのみ使用し外部から直接使用しないようにする。外部との やりとりは、初期化サブルーチンと実行サブルーチンだけを通じて行う。

初期化サブルーチンで共有変数の初期値の設定を行う。

 実行サブルーチンはパッケージ型モジュール内に1個もしくは複数個あり、パ ッケージ型モジュールのメインの部分である。引数を通じて外部からデータを入 力し、計算を行い、引数を通じて外部に必要なデータを出力する。

http://www.mri-jma.go.jp/Project/mrinpd/coderule.html

「外部とのやりとりは引数でだけ」。

つまり気象庁コーディングルールでは、「別の module の共有変数を呼び出して値を変える」ことは認めていない。別の module の共有変数の値を変えるには、「その module 内の subroutine を call する」しかない(function は基本使わないので置いとく)。

「パッケージ型は完全にカプセル化」し、グローバル変数の参照は変数参照型モジュールからのみ。変数参照型モジュールって何よ、というと、

変数参照型モジュールの共有変数は、プログラム実行時に最初に一度だけ値を 設定し、それ以後変更しないのが理想である。値の変更がある変数については、 変数参照型モジュールではなく、引数でサブルーチンに渡す

http://www.mri-jma.go.jp/Project/mrinpd/coderule.html

ってことは…必然的に引数多めになるよねこれ、やっぱり。それしかないのかなぁ。それか、「パッケージを大きくする」かなぁ。

牛島本で言うところの「グローバル変数モジュール」の最大の問題はやはり「いつどこでだれがその変数を変えてるかわからない」ということだと思う。それを排除する意味で「変数参照型モジュール」は賢いけれど、モジュール外で値を変えられない…

というか、「値を変えるような subroutine 群は一つのモジュールに突っ込め」ってことなのかなぁ。まぁそうすりゃ楽だよなぁ。パッケージ型モジュールのスコープをそうやって決めてしまう(いまより大きめになる)のが一番だろうか。でも「巨大なパッケージ内の共有変数」って結局どこで変更されたかわかりづらいよね…意味無いじゃん。

なんで引数が嫌かって言うと、理由はいくつかあるけれど、最大のものは、これも牛島本にあったことなんだけれど、「いくつも subroutine の階層をくだっていかないといけない場合に、途中の階層では使わない変数も全て引数としてバケツリレーしなきゃいけない」ってこと。同じくらい嫌なのが、引数で配列を渡す場合、allocatable だとかなり気を使うってこと。

あとは形状引継ぎ配列は使わないので、全部の subroutine に配列の上下限をキッチリ書いているわけだけれど、一箇所でも上限か下限を変えたら全部書きなおさなきゃいけないってのも地味にメンドイ。まぁ一括置換しろって話だけどそれってなんか…。include使うのも微妙だし。


いろいろ考えたけど、モジュールをいくつかマージしてやや大きめなパッケージ型モジュールを作ることにした。グローバル変数モジュールの採用はまだ保留中。そもそもF77であったcommon文を一度グローバル変数モジュールにしたけれど、変数の値の変更タイミングがわかりづらすぎて使用を止めた経緯があるので。

追記:セーブとロードの subroutine のみ特別扱いとして、パッケージ型モジュールからの use を許すかどうか迷ってる。こうすると、無駄な途中の引数バケツリレーをかなり省略できる。ただ、このためには private を public にしなきゃいけないのが微妙…。