ブログ 約4分

SQLite:シンプルに保つ技術

Share this article
SQLite:シンプルに保つ技術

最初のチェックインから16年後、SQLiteは世界で最も広く導入されているデータベースエンジンになりました。このようなオープンソースプロジェクトは、コードを理解しやすく保守しやすくする方法を学ぶための優れた教材です。

SQLiteコードベースに関するいくつかの事実を探ってみましょう。そのために、まず次のコードスニペットから始めます。

sqlite

この関数からは、いくつかの点が読み取れます。

  • 関数は static として宣言されている。
  • 関数はエラーコードを返す。
  • 関数のパラメータは少数しかない。
  • 関数はできるだけ早く終了する。
  • 特定の条件を確認するためにアサーションが使われている。
  • グローバル変数は使われていない。
  • 変数名が分かりやすい。
  • メソッドが短い。
  •  本体内に不要なコメントがない。
  • 関数本体が適切にインデントされている。

SQLiteのソースコードを探索すると、実装の一貫性に気づきます。同じベストプラクティスがすべての関数に適用されています。

SQLiteコードベースから学べるベストプラクティスをいくつか紹介します。

データモデルは構造体に格納する

Cプログラミングでは、関数は変数を使って処理を実行します。これらの変数には次のようなものがあります。

  • 静的変数。
  • グローバル変数。
  • ローカル変数。
  • 構造体の変数。

どのプロジェクトにもデータモデルがあり、それは多くのソースファイルから使用される可能性があります。グローバル変数を使うことも1つの解決策ですが、良い方法ではありません。データを構造体にまとめる方が望ましい方法です。

CQLinq と CppDepend を使って、定義されている構造体を検索してみましょう。

sqlite22

データモデルを指定するために、多くの構造体が使われています。

関数は短く簡潔に保つ

以下は、 LinuxコーディングスタイルのWebページからの、関数の長さに関するアドバイスです。

Functions should be short and sweet, and do just one thing.  They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the
complexity and indentation level of that function.  So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.

コード行数が30行未満の関数を検索してみましょう。

sqlite5

90%を超える関数が30行未満です。

カプセル化

カプセル化とは、実装内部の関数とデータを隠蔽することです。Cでは、static キーワードを使ってカプセル化を行います。これらのエンティティは、ファイルスコープの関数と変数と呼ばれます。

次のCQLinqクエリを実行して、すべての static 関数を検索してみましょう。

sqlite

ご覧のとおり、多くの関数が static として宣言されています。

関数のパラメータ数

NbParameters > 8 の関数は呼び出しにくく、パフォーマンスを低下させる可能性があります。別の方法は、引数渡しを処理する専用の構造体を用意することです。

sqlite6

パラメータが8個を超える関数はごく少数です。

ローカル変数の数

NbVariables が8を超える関数は、理解と保守が難しくなります。NbVariables が15を超える関数は非常に複雑であり、より小さなメソッドに分割すべきです(ツールによって自動生成された場合を除きます)。

sqlite7

ローカル変数が15個を超える関数はごく少数です。

複雑な関数を定義しない

複雑な関数を検出するメトリクスは多数あります。NBLinesOfCode、パラメータ数、ローカル変数数は基本的なものです。

複雑な関数を検出するための、ほかにも興味深いメトリクスがあります。

  • 循環的複雑度は広く使われている手続き型ソフトウェアメトリクスで、プロシージャ内で取り得る判定の数に等しくなります。
  • ネスト深度はメソッドに対して定義されるメトリクスで、メソッド本体内で最も深く入れ子になったスコープの最大深度に関係します。
  • 最大ネストループは、関数内のループネストの最大レベルに等しくなります。

これらのメトリクスで許容される最大値はチームの判断に依存し、標準的な値はありません。

リファクタリングの候補となる関数を検索してみましょう。

sqlite8

複雑と見なせる関数はごくわずかです。

const正確性を保つ

Cでは、 const キーワードを使って、変更できないオブジェクトをパラメータとして渡したり、メソッドがオブジェクトを変更しないことを示したりできます。適切な場所すべてで const を使うことは「const正確性」と呼ばれます。最初は難しいですが、const を使うとコーディングスタイルが本当に引き締まります。

少なくとも1つの const パラメータを持つ関数を検索してみましょう。

sqlite9

関数の結合度

多くの関数を使用する関数は、理解と保守が非常に難しくなります。関数の求心性結合度(efferent coupling)は最小限に抑えることが推奨されます。

SQLiteでは、高い求心性結合度を持つ関数はごくわずかです。

sqlite10

可能な場合は関数を早めに終了する

関数からの早期終了、特に関数冒頭のガード節による早期終了は、その後のロジックを単純化するため推奨されます。

SQLiteのソースコードでは、このベストプラクティスがほぼすべての関数に適用されています。

まとめ

有名なオープンソースプロジェクトを探索することは、常にプログラミングスキルを高める良い方法です。プロジェクトをダウンロードしてビルドする必要はありません。GitHub上でコードを探索するだけで十分です。

Share this article