データ構造の操作とロジックのコードは区別しよう。


 C++言語でSTL(Standard Template Library)を使ってソースコードを書くようになって感じたことは、データ構造の操作は標準のライブラリに用意されているので、ロジックの部分がわかりやすくなったということだ。

独自データ構造のライブラリ

 STL以前の独自のデータ構造を使っているコードを読むと、データ構造の操作とロジックとの区別がつきにくいことが、コードの理解を妨げていることに気づいた。たとえば、領域を確保済みの構造体の配列を使っているコードがあっても、初期値の代入のコードや、i番目の配列要素に有効な値を代入するコード、有効な値がどこまで入っているのか、どこから無効な値になっているのかを知るマジックナンバー、それらが構造体をべたに操作しているとデータ構造の操作と、実装しているロジックとの区別がつかないということです。

結局必要になる共通操作

 ですから構造体配列などの独自のデータ構造を使う場合でも、データの追加、データの代入、データのコピー、データの表示、データの削除などを行う関数を結局は作らなくてはならない。ましてやmemcpyで構造体データのコピーをすることはしてはいけない(注1)。Cの範囲の中でコーディングする場合でも、それらのデータ構造に対する操作(事実上のメソッド)を実装(カプセル化)してテストしておくことが必須になる。そのようなメソッドをテストしておくことで、安心して構造体配列が使えるようになる(注2)。

データ構造の操作とロジックを分離しよう

 そのような事実上のメソッドが準備されているときには、データ構造の操作とロジックのコードは明確に区別できるようになり、プログラムのロジックがわかりやすくなる。
 ですから、データ構造の操作とロジックのコードは区別するようにソースコードを書いていきましょう。

レガシーコードの改善

レガシーコードを改善していく際には、リファクタリングから始めていくこと。いきなりSTLで書き換えようとしないこと。レガシーコードに単体テストを追加していくこと。単体テストによってそのコードの正しさを確保しよう。レガシーコードが何をしているのか、十分に自明なコードになりきる前にSTLを使って書き換えようとはしないこと。

追記:
 Python屋の私としては、Pythonの標準的なデータ構造、リスト(C++
のstd::vectorに相当)、辞書(C++のstd::unsorted_map)に相当を使うことで、標準的なデータ構造を使うように仕向けられていたと言える。またPythonの豊富な標準ライブラリ群は、C++のBoostライブラリを使ってOS依存性のない標準的な豊富な信頼できるライブラリを使うのと同様な効果をもたらしていたといえるだろう。
「すべてをゼロから書き直したい衝動に駆られることもありますが、その誘惑に打ち克たなければなりません。」出典 [06] リファクタリングの際に注意すべきこと

追記:
 プログラム開発していると、データ構造の操作とロジックのコードでは、データ構造の操作の方がコードが確定していきやすい。データ構造の操作が確定していくとそのコードは十分に枯れたコードになりやすい。一方アルゴリズムは、既に完成したアルゴリズムを移植しているのではない限り、試行錯誤の関係で幾度となく変更ということになりやすい。だからデータ構造の操作の分を別ファイルにしておくとよい。

注1:memcpyでは意図しない浅いコピーを行ってしまいやすく、ある構造体の文字列(の開始位置のポインタ)とコピー先の構造体の文字列(の開始位置のポインタ)とが同じになってしまい、一方を変更すると他方にも変更が及ぶ。
 特に、memcmpを構造体の比較にはおこなってはならない。
 EXP04-C. 構造体を含むバイト単位の比較を行わない。

注2:STLを使えばいいのではないかというのが、個人的見解です。既存のプログラムで、構造体配列をSTLに置き換えるには、なかなか手をつけにくい場合もあります。そのような場合には、事実上のメソッド関数を追加していって、構造体配列に対するむき出しのデータ構造への操作を減らしていくことだと考えています。