ぷろみん

プログラミング的な内容を扱ってます

依存関係を隠さないで

モチベーション

依存関係を隠そうとして依存関係を深くしないように。

どちらが良い設計?

Foo::Foo() {
    texture_ = Singleton::GetInstance()->GetTexture();
}

// 依存性無しに構築できる?
Foo foo;
Foo::Foo(const Texture &texture):
    texture_(texture)
{}

// 構築する為にTextureが必要
Foo foo(texture);

シングルトンを使った方は一見依存性を減らせたかのように見えます。シングルトンを使った方が良いのでしょうか?

生成の過程を追う

Textureを作る為に画像のパスをコマンドラインから取得してみましょう。

Texture texture(argv[1]);
Foo foo(texture);

シングルトンの構築時にコマンドラインを取得できないので、シングルトンもコマンドラインを受け取らないといけません。

Singleton::GetInstance()->SetPath(arg[1]);
Foo foo;

ここで奇妙な事が起きています。Fooの構築の前にSetPathが必須なのにも関わらずそれが明示されていません。Fooの処理が失敗する理由が外部から分からない、内部のソースを読む事が必須のプログラマに負担をかけるクラスになってしまっているのです。

必須要素はコンストラクタ

同様な理由から必須要素のsetterも利用者に負担をかけます。せめて処理が失敗する理由を書いたassertがあれば良いのですが。良い設計ではコンストラクタの要求を満たしていくだけで機能するように依存関係が明示的に示されています。構築された時点で常に有効なクラス設計を心がけましょう。