...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

クラス間依存関係の除去 第3.5回 - ファクトリメソッドパターンを適用しない理由

さて,前回はファクトリパターンを適用しました.
ところが,このままではファクトリクラス AnimalFactory が生成する具象クラス Dog/Cat に依存しているので,具象クラスを追加する度に AnimalFactory の修正が必要になります.


一般的に,ファクトリクラスと具象クラスの依存関係を断つ方法はいくつかあります.
例えば,ファクトリメソッドパターンを適用してみましょう.

import std.stdio;

// ++ animal.d
interface IAnimal
{
    void bark();
}
class AnimalTemplate(T) : IAnimal
{
    private T t;
    this()
    {
        t = new T;
    }
    void bark()
    {
        t.bark();
    }
}
// ++ dog.d
class Dog
{
    void bark()
    {
        writefln("wan!wan!");
    }
}
// ++ cat.d
class Cat
{
    void bark()
    {
        writefln("nyan!nyan!");
    }
}
// ++ animalfactory.d
//import animal;
abstract class AnimalFactory
{
    IAnimal create()
    {
        return this.createConcrete();
    }
    protected abstract IAnimal createConcrete();
}
// ++ dogfactory.d
//import animal, dog;
class DogFactory : AnimalFactory
{
    protected AnimalTemplate!(Dog) createConcrete()
    {
        return new AnimalTemplate!(Dog);
    }
}
// ++ catfactory.d
//import animal, cat;
class CatFactory : AnimalFactory
{
    protected AnimalTemplate!(Cat) createConcrete()
    {
        return new AnimalTemplate!(Cat);
    }
}
// ++ client.d
//import animal, animalfactory;
class Client
{
    private AnimalFactory factory;
    this(AnimalFactory factory)
    {
        this.factory = factory;
    }
    void perform()
    {
        IAnimal animal = this.factory.create();
        animal.bark();
    }
}
// ++ main.d
//import client, dogfactory, catfactory;
void main()
{
    Client client1 = new Client(new DogFactory());
    client1.perform();
    Client client2 = new Client(new CatFactory());
    client2.perform();
}

以前は, AnimalFactory の中に具象クラスを切り替えるための switch 文がありました.
ファクトリメソッドパターン適用後は, switch 文の振る舞いをポリモーフィズムによって実現するために,各具象クラスのインスタンスを生成する DogFactory/CatFactory を追加してみました.


また, Client クラスも追加しています.
これがないと;

//import animal, animalfactory, dogfactory, catfactory;
void main()
{
    AnimalFactory df = new DogFactory;
    IAnimal animal = df.create();
    animal.bark();
    AnimalFactory cf = new CatFactory;
    animal = cf.create();
    animal.bark();
}

結局,インスタンスの生成とメソッド呼び出しを同じ場所に書くことになり,AnimalFactory を親クラスとするポリモーフィズムの恩恵をまったく受けることができません.
以前は main 関数内で行っていた処理が,そのまま Client.perform 関数に移ったことになります.


このように,確かに AnimalFactory クラスと Dog/Cat クラスの依存関係を絶つことができますが,クラスの関係が複雑です.
それに,main.d ファイルの中で具象クラスに対応する DogFactory/CatFactory クラスのインスタンスを生成しているので,具象クラスを追加するときにはまた main.d を書き換えなくてはいけません.
なんだか本末転倒です.


そこで,この連載ではファクトリメソッドパターンは使いません.
その代わり,ファクトリクラスと具象クラスとの依存関係を逆転させることによって,ファクトリクラスから具象クラスへの依存をなくします.
名著 Modern C++ Design で述べられている方法がヒントです.

  • 2007/07/07 改訂