手離れのいい外部仕様を作成しよう


プログラムの開発を分業して行っていて気づいたことがある。新たに加わった人がメンテナンスしやすいモジュールと、そうでないモジュールとがあることだ。メンテナンスしやすいモジュールでは、加わったその日にコードの改善が可能である。そうでないモジュールでは、数ヶ月たっても、近寄りがたいものとして意識され続ける。手離れのいい外部仕様を作成して、自分の仕事を減らしていこう。 

新たに加わった人がメンテナンスしやすいモジュールの特徴

  • 外部仕様が明確である。
  • ドキュメンテーションコメントが充実していている。
  • 引数のin, out, inoutの区別が明確である。 @param[in], @param[out]
  • const 属性が可能な引数にはconst 属性がついている。
  • 関数定義の際に、可能な場合には引数を参照渡しを用いている。
  • 単体テストがされている。
  • include するヘッダファイルの数は抑制されている。
  • 言葉が誤解を減らすように選ばれている。
  • #defineでマクロ定数を使うよりはenum型を使っている。
  • std::vectorやstd::map型を使っている。
  • namespace やクラスを使うことで、変数のスコープが狭くなるようにしている。
  • 変数名や項目名が、整理された名前になっている。(最初からいい名前だったわけではなく、考えた末に名前が変更されている)
  • 複数ソースコードにまたがる#ifdef マクロ定数のマクロ定義をソースコード中に書くのではなく、CMakeLists.txt や .sln, *.projファイルなどで記述する。そうすることで、.cpp, *.h自体は変更を受けなくする。バージョン管理の際に、ソースコードが変更にならない方がよい。

新たに加わった人がメンテナンスしにくいモジュールの特徴

  • 外部仕様が不明確である。
  • 外部仕様の一番有効な記述が何であるのかが、ソースコードのドキュメンテーションコメントからたどることができない。
  • ソースコード以外のドキュメントでも、有効なドキュメントと古くなって乖離してしまったドキュメントが混在してしまっている。
  • ドキュメンテーションコメントが不足していている。
  • 引数のin, out, inoutの区別が不明確である。
  • const 属性が可能な引数でもconst 属性がついていない。
  • 関数の定義の際に、参照渡しが可能な場合でも、とにかくポインタ渡しで定義されている。
  • 単体テストがされていない。
  • include するヘッダファイルの数はとにかく増えてしまう。
  • 誤解しやすい言葉がそのままドキュメンテーションコメントに残ってしまっている。
  • 全体像を知った上でそのモジュールを理解することが求められている。
  • #defineでマクロ定数を使っている。
  • std::vectorよりも固定サイズの配列を使う。
  • std::map型よりもcase文やif(){}else if(){} else if(){} else{}を使う。
  • cppファイルであっても、C言語の仕様の範囲内で書けることを優先している。 そのため、変数のスコープが広くなりがちであって、global 変数や externの記述が多く残ってしまっている。
  • CMakeってなんだ。

pythonで最初にプロトタイプして、アイディアを検証してからC++の仕様を定めると手離れのいい外部仕様になるように経験的に感じている。

手離れのいい外部仕様を作ることは、そのライブラリのメンテナンスが楽になるだけではなく、そのライブラリを活用してもらいやすくなることでもある。

extern を乱発しまくっているコードは、単体テストがしにくい。
単体テストしようとして、必要になってしまったモジュールをどんどんリンク対象に増やしていくと、どんどん単体テストから外れていってしまう。extern を乱発しまっくているのは、問題がある可能性が高い。

extern をC++のライブラリから取り除く
を作成しました。


付記:
自作ライブラリでも画像データ構造を引数として画像処理の関数を実装しよう
を執筆しました。仕様を作成するときのヒントになれば幸いです。