Edited at

「野放しのグローバル変数」からクラスを見つけ出す

More than 1 year has passed since last update.

 ソースコードのリファクタリングでは「野放しのグローバル変数」というまずいコードの項目がある。

 ソースコードに機能を拡張しようとしている範囲の中に「野放しのグローバル変数」が見つかった。そこからグローバル変数を減らした実作業の中で感じたことを述べたいと思う。


・グローバル変数として残ってしまったわけがある。

 一つの関数の中でしか使われないような変数ならば、そもそもグローバル変数として前任者が書いてしまうこともなかっただろう。

 少なくとも、2つ以上の関数がその変数を用いているということだ。

コード上では依存性のない2つ以上の関数が実は無関係ではなく、密接な関係にあるということだ。「野放しのグローバル変数」に値を与えている側と値を参照している側とがあるだろう。


・隠れていたクラスを発見する

 私が作業したその例では、2つ以上の別々の関数では意味合いが不明だったまとまりが、実は意味のまとまりを持つ単位だった。1つはしかるべき設定を行い、もう一つはその設定を利用したデータの変換を行うものだった。

 値を与えている側がコンストラクタであり、値を参照している側はアクセッサ、コンバータなどとして実装できそうである。「野放しのグローバル変数」はそのクラスの中のデータメンバーとして落ち着くことになった。

 そのような意味が明確になったことで、そのクラスに関するテストを記述できそうなくらいに意味が明確になった。


・インスタンスのおき場所を考える

 次は、発見したクラスのインスタンスを、ソースコードのどこに置くかという点である。

 1つの関数のスコープの中で記述できれば幸いである。

 しかし、時として、そのインスタンスのスコープがグローバル変数として残ってしまうことがある。しかし、それでも、元々の「野放しのグローバル変数」から、グローバルスコープのインスタンスのデータメンバーとしてスコープが狭まっていることに間違いはない。


さあ次の「野放しのグローバル変数」に立ち向かおう

 これで1つの敵に打ち勝ったが、「野放しのグローバル変数」という敵はまだまだ多いままだ。自分が担当する改変に影響する分野の「野放しのグローバル変数」に立ち向かおう。グローバル変数の中から隠れていたクラスを発見するのは簡単な作業ではない。得意としない分野では、間違った解釈をしてしまう危険もある。動いているコードを壊してしまう危険もある。バージョン管理し、テストを書き、設計の健全性を確保しながら進めていこう。

追記:


見つけ出したクラスに関連するマクロ定数

 今回見つけ出したクラスの中でマクロ定数が使われているかもしれない。そのようなマクロ定数が、別のソースファイルの中で残っていることはないでしょうか? そのような定数は実はマクロである必要はなく、クラスのメソッドで与えられる定数であってよいということはないでしょうか? そのような場合にはマクロ定数から、クラスのメソッドにより与えられる定数としましょう。

 あるクラスのメソッドの戻り値、あるいは@param[out]である引数の中に、マクロ定数を用いて事前に大きさを知っていなければならないという制約のあるプログラムを書いていないでしょうか?

 プログラムの書き方次第では、そのような制約を外すことができます。そうすると、今回見つけ出したクラスにかかわるマクロ定数を大幅に減らすことができるはずです。


実際の泥臭い作業:

  既存のコードの中では、メソッドとして切り出すべき部分が、そうでない部分ときれいに分かれているとは限りません。

//期待される型 function(引数のリスト){//「こんな引数の関数があるといいな」の始まり

// return 戻り値
//} //「こんな引数の関数があるといいな」の終わり

といったコードをコメントとして書き加えてみます。

既存のコードで、この関数の前の処理になるべきもの、

この関数の処理の後になるべきものを、見極めていきます。

#if 1

元々のコード
#else
新しく作った関数を呼び出すコード
#endif

のようにしてゆき、少しずつ動作を確かめながら関数化をしていきます。

#if文を切り替えても動作することが確認できたら、関数の切り出し・メソッドの切り出しは成功です。

このとき、関数やメソッドの意味が直感的にわかりやすい意味の塊になっているのかに留意します。

何でもかんでもカプセル化するのが良いのではなくて、ロジックをたどるときに見えた方が理解しやすいものが明確に見えるようにした方がよいと私は考えます。

ですから、メソッドの引数がまったくなくなってしまうほどの抽象化を進めた複数のメソッドというのは、私の好みではありません。


クラスへのテストを実装する

 何らかの意味のまとまりがあってクラスにしたコードなのですから、その意味のまとまりにテストできるはずなのです。コードはどれほど品質が確保されているかが価値なのです。品質が確保されている短いコードこそ価値があるのです。

 だいぶ疲れていて、もう終わりにしたいと思っているかもしれませんが、ここは意地の見せ所です。

クラスへのテストをCppUnitなどの単体テストの枠組みに入れるときに、テストするモジュール、とりわけヘッダファイルに、テストするのに邪魔な宣言がある場合には、ヘッダファイルを分割するのがよいと思います。