編集中です m(..)m
第3回では,シンプルなファクトリクラス AnimalFactory を追加することによって,利用側の main.d ファイルが具象クラス Dog/Cat クラスに直接依存することを避けました.
ところが,今度はファクトリクラス AnimalFactory が具象クラス Dog/Cat に依存しているので,具象クラスを追加する度に AnimalFactory の修正が必要になりました.
第3.5回では,ファクトリクラスから具象クラス Dog/Cat への依存を絶つ方法として,ファクトリメソッドパターンを適用してみました.
しかし,クラスの数が増えて関係が複雑になってしまい,また,具象クラスを追加するときに main.d ファイルの書き換えがまた必要になってしまいました.
そこで,今回は,第3回のコードを元に,ファクトリクラス AnimalFactory から Dog/Cat クラスへの依存関係を逆転させます.
これにより,具象クラスを追加するときに,既存のファイルを変更しなくてもいいようになります.
まずは,元のコードを示します.
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, dog, cat; class AnimalFactory { static IAnimal create(uint code) { switch (code) { case 0: return new AnimalTemplate!(Dog); case 1: return new AnimalTemplate!(Cat); } } } // ++ main.d //import animal, animalfactory; void main() { IAnimal animal = AnimalFactory.create(0); animal.bark(); animal = AnimalFactory.create(1); animal.bark(); }
次に,ファクトリクラス AnimalFactory から Dog/Cat クラスへの依存関係を逆転させたコードを示します.
import std.stdio; import std.string : toStr = toString; // 名前衝突回避のため // ++ 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!"); } } // ++ dogregister.d //import animalfactory, animal, dog; class DogRegister { static this() { AnimalFactory.regist!(AnimalTemplate!(Dog))(0); } } // ++ catregister.d //import animalfactory, animal, cat; class CatRegister { static this() { AnimalFactory.regist!(AnimalTemplate!(Cat))(1); } } // ++ animalfactory.d //import animal; class AnimalFactory { private static IAnimal delegate()[uint] factoryMethods; static void regist(ConcreteType)(uint code) { if ((code in factoryMethods) != null) { throw new Exception("Code '" ~ toStr(code) ~ "' is already registered."); } factoryMethods[code] = delegate IAnimal(){ return new ConcreteType; }; } static IAnimal create(uint code) { if((code in factoryMethods) == null) { throw new Exception("Code '" ~ toStr(code) ~ "' is not registered yet."); } return factoryMethods[code](); } } // ++ main.d //import animal, animalfactory; void main() { IAnimal animal = AnimalFactory.create(0); animal.bark(); animal = AnimalFactory.create(1); animal.bark(); } // おまけのテストコード unittest { class Bad { void no_bark() {} } // Runtime error; Code '0' is already registered. AnimalFactory.regist!(AnimalTemplate!(Cat))(0); // Rutime error; Code '3' is not registered yet. auto temp = AnimalFactory.create(3); // Complie error; 'Bad' class do not have 'bark' method. AnimalFactory.regist!(AnimalTemplate!(Bad))(2); }
まず,注目していただきたいのは,既存の多くのファイル animal.d/dog.d/cat.d/main.d を変更していないことです.
逆に,変更したファイルは,animalfactory.d ただ一つです.
また,dogregister.d/catregister.d の2つのファイルを追加してあります.
詳しい説明に続く...