Pimpl とかファクトリとか – C++ のための API デザイン
otoco プロジェクトの開発に着手するにあたって、私はまだ C++ でのライブラリ開発を 1 からコーディネートした経験がなかったので、クロスプラットフォームに対応したライブラリ API の開発手法を学ぶ必要があると感じました。ちょうどいい感じの教材が割りと最近出ていたようで、早速購入し、勉強を進めています。
C++11 についても若干触れられているようで (原著執筆当時はまだ C++0x と呼ばれていた模様…)、この手の教材の中では比較的情報が新しい方なんではないかと思われます。
実はこの本を買って勉強し始めたのはもう結構前 (確か前原にいた頃… 昨年の暮れ頃?) なのですが、ここしばらく本業やら引っ越しやらが忙しくてなかなか手を回せずにいたので、久しぶりの着手ということで、すでに履修していた第3章の、 Pimpl イディオムとファクトリメソッド辺りを復習してみました。
今度こそ本腰を入れてちゃんとやろう、ということで、練習用に書いたサンプルプログラムは github に up しております。 git の練習も兼ねているのですが…。
Pimpl イディオム
C++ でライブラリを実装するのに際して最も基本的とも言うべきテクニックです。 2章の中で、優れた API の特徴について議論されていますが、その中でも Pimpl イディオムは、プラットフォーム独立性を高める働きと、結合度を削減する働きを担っています。本書のサンプルではタイマーを取り上げ、 Unix の getTimeOfDay(2) システムコールと Win32 の GetTickCount()
API の両方に対応する場合に、 Pimpl イディオムを用いて実装の詳細をヘッダーファイルから .cpp
ファイルへ隠すというものでした。
私が書いたサンプルもほぼ同内容です (メインルーチンはお馴染みの? あほプログラムでやんす)。強いて違いを言えば、 Pimpl を std::unique_ptr
に突っ込んでいること (本書では boost::scoped_ptr
を採用していました) ぐらいでしょうか。
ファクトリメソッド
本書では Pimpl イディオムの次にシングルトンパターンを解説していますが、今回はここは一旦飛ばして (書かれていることは有用なので、目を通すことはおすすめします)、ファクトリメソッドの方をさらってみました。
本書で紹介されているファクトリメソッドは、 GoF のファクトリメソッドパターンとは若干異なるようで、 Creator の継承と Product の継承を連動させて云々、というものではなく、単に値による指定で生成するオブジェクトの種類を切り替えるという類のもののようでした。これはこれで、例えばプラグインの実装なんかでは役に立ちそうなテクニックだとは思います。
拡張可能なファクトリの応用として、ファクトリ登録を生成されるオブジェクトのクラス毎に行うようにし、 Product の追加が行いやすいような設計にしてみました。この用途に限れば登録解除は不要だろうということで、登録用のメソッドのみ用意。ファクトリ名とコールバックの保存には、グローバル変数ではなく、関数内 static
変数として用意した std::unordered_map
に格納することで、コンパイル単位をまたいだ非ローカル静的オブジェクトの初期化順に伴うSEGVりを回避しています (この辺のことはそれこそ本書のシングルトンの項にて、 Scott Meyers の言葉を引用しつつ説明されています)。
また、せっかくなので極力 Pimpl を用いるようにしてみました。プラットフォーム独立性も重要ですが、それを考慮する必要のない場面でも、 private
メソッドの追加・変更に伴う再コンパイルを気にする必要がなくなることによるメンテナンス性の向上 (修正・改変・リファクタリングのやりやすさ) は非常に重要です。今回作った中では、特に Base64Converter
クラスの実装がまぁまぁいいサンプルになったんじゃないかと思います (…Base64Map
クラスも .cpp
ファイルの中に隠しちゃってもよかったかもなぁ…)。
今回はここまで。次回があれば、今度はラッピングパターン、オブザーバーパターンあたりをさらえればと思います。
2014 年 6 月 19 日 by 村山 俊之
2014 年 8 月 6 日 6:06 PM
[...] ライブラリ API の設計手法を学ぼうシリーズの第2弾です。前回の記事はこちら。以下の教材を利用しています。 [...]