第14回目のノータブルコードでは、collectdのコードで印象に残ったヘッダのコードを紹介します。
印象に残ったヘッダファイルは、メタデータを定義しているmeta_data.hです。 一部を抜粋すると以下のようになっていました。
struct meta_data_s;
typedef struct meta_data_s meta_data_t;
あくまでmeta_data_s
とmeta_data_t
のみ宣言され、その実体については隠蔽されています。
meta_data_t *meta_data_create(void);
meta_data_t *meta_data_clone(meta_data_t *orig);
int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig);
void meta_data_destroy(meta_data_t *md);
int meta_data_get_string(meta_data_t *md, const char *key, char **value);
int meta_data_get_signed_int(meta_data_t *md, const char *key, int64_t *value);
int meta_data_get_unsigned_int(meta_data_t *md, const char *key,
uint64_t *value);
int meta_data_get_double(meta_data_t *md, const char *key, double *value);
int meta_data_get_boolean(meta_data_t *md, const char *key, bool *value);
そして、meta_data_t
のポインタを介してすべてアクセスするようになっています。
一方で実際の構造体の内容については次のようにmeta_data.cにあります。
struct meta_data_s {
meta_entry_t *head;
pthread_mutex_t lock;
};
このようにすることで、実装の詳細は隠蔽しつつも、必要なアクセス手段をmeta_data_xxx
という各種関数として提供しています。
collectdではたくさんのプラグインが提供されています。もし内部実装の詳細に依存するコードのプラグインがたくさんあれば、変更のたびにプラグイン側も修正してまわる必要がでてきます。 collectdには170近いプラグインが存在するので、そのようなコードはメンテナンスしやすいコードとは言えません。 実装の詳細を隠蔽しておくことで、内部実装が変わってもプラグイン側への影響が少なくなり、メンテナンスしやすいコードになります。
おまけ
とはいえ、実装の隠蔽はすべて徹底しているわけではなく、notification_meta_t
は実装の詳細もplugin.hでまるっと公開していたりします。これは推測ですが、2014年のコミット以降変更されていないので、単にnotification_meta_t
は初期の実装をそのまま使い続けているためとみられます。
notification_meta_t
に依存しているプラグインもそれほど多くないので問題にはなっていないのでしょう。
typedef struct notification_meta_s {
char name[DATA_MAX_NAME_LEN];
enum notification_meta_type_e type;
union {
const char *nm_string;
int64_t nm_signed_int;
uint64_t nm_unsigned_int;
double nm_double;
bool nm_boolean;
} nm_value;
struct notification_meta_s *next;
} notification_meta_t;
まとめ
内部構造を必要以上に見せないようにAPIを整備すると、変更に強いメンテナンスしやすいコードになります。 以上、collectdのコードで印象に残ったヘッダのコードの紹介でした。