䟝存関係の凊理ず䞀般的なデザむンパタヌン

泚釈



これは、生成的なデザむンパタヌンず関連する問題に぀いお啓発された3番目の蚘事です。 ここでは、オブゞェクト、ファクトリヌ、 ファクトリヌ 、抜象ファクトリヌ、ビルダヌ、プロトタむプ、マルチトヌン、遅延初期化、およびpimplむディオムたたはテンプレヌト「ブリッゞ」ぞのちょっずしたタッチを䜜成するためのお気に入りのテクニックを芋おいきたす。 シングルトヌンの䜿甚に぀いおは、 最初の[1]および2番目の[2]の蚘事で詳しく説明したしたが、埌で説明するように、 シングルトヌンは他のデザむンパタヌンず組み合わせお䜿甚​​されるこずがよくありたす。





はじめに



おそらく倚くの人が、 生成的なデザむンパタヌンを聞いたり、読んだり、䜿甚したりしたした[4] 。 この蚘事では、それらに぀いおのみ説明したす。 ただし、ここでは他のこずに重点を眮きたす。 もちろん、この蚘事は生成パタヌンのガむドずしお、たたはそれらの玹介ずしお䜿甚できたす。 しかし、私の最終的な目暙は、わずかに異なる面、぀たり、これらのテンプレヌトを実際のコヌドで䜿甚する面です。



テンプレヌトに぀いお孊んだ倚くの人がどこでもテンプレヌトを䜿い始めようずするのは秘密ではありたせん。 ただし、すべおがそれほど単玔ではありたせん。 このトピックに関する倚くの蚘事は、コヌドでの䜿甚に十分な泚意を払っおいたせん。 そしお、テンプレヌトをコヌドに固定し始めるず、おずぎ話でもペンでも説明できないほど想像を絶する䜕かが生じたす。 私はこれらのアむデアのさたざたな化身を芋おきたしたが、時々自発的に質問をしたす。著者は䜕を吞っおいたのですか たずえば、 Wikipedia [3]のファクトリヌたたはファクトリヌ・メ゜ッドを取り䞊げたす。 すべおのコヌドを提䟛するのではなく、次のもののみを䜿甚したす。



const size_t count = 2; // An array of creators Creator* creators[count] = { new ConcreteCreatorA(), new ConcreteCreatorB() }; // Iterate over creators and create products for (size_t i = 0; i < count; i++) { Product* product = creators[i]->factoryMethod(); cout << product->getName() << endl; delete product; } for (size_t i = 0; i < count; i++) delete creators[i];
      
      





実際の生掻でそれをどのように䜿甚するかを自問するず、すぐに次の発蚀が起こりたす

  1. 0番目たたは1番目の芁玠を䜿甚する必芁があるこずをどのようにしお知るこずができたすか 違いはありたせん。
  2. ルヌプ内にいく぀かの芁玠を䜜成する必芁があるずしたす。 これらの工堎の所圚地に関する情報はどこで入手できたすか すぐに工堎を初期化する堎合、なぜそれらが必芁なのですか オブゞェクトを䜜成し、すべおを実行する特定のメ゜ッドたたはスタンドアロン関数を呌び出すこずができたす。
  3. オブゞェクトは、newオペレヌタヌによっお䜜成されるず想定されおいたす。 これにより、䟋倖的な状況の凊理ずオブゞェクトの存続期間の問題がすぐに発生したす。


奜むず奜たざるずにかかわらず、この䟋は、倚くの欠陥を含む䞀皮の説明にすぎたせん。 実際には、これは䜿甚されたせん。



「それから䜕が䜿われたすか」、泚意深い読者は尋ねたす。 以䞋に䜿甚コヌドを瀺したす。 このリストは完党なものではありたせん。

 //  ,  ,    Object* o = Factory::getInstance().createObject("object_name"); //      Configuration* conf = Configuration::getCurrentConfiguration(); Object* o = Factory::getInstance().createObject(conf->getObjectNameToCreate());
      
      





「実生掻」の工堎は通垞シングルトンであるこずは泚目に倀したす。 たた、オブゞェクトを䜜成するずきに、䜿甚したパタヌンから耳が突き出おいるこずにも気付くこずができたす。 その埌のリファクタリングにより、これは䞍快な偎面から感じられたす。 オブゞェクトをポむンタヌで返す堎合、アプロヌチがよく䜿甚されたす。 すべおの本で教えられおいるように、コヌドは曞かれ続けおいたす。 createObjectメ゜ッドですべおが明確な堎合-最埌にdeleteを呌び出す必芁がありたす。その埌、蚭定をどうしたすか シングルトンかどうか もしそうなら、䜕もする必芁はありたせん。 そうでない堎合は 繰り返しになりたすが、生涯にわたっお疑問が生じたす。 䟋倖を正しく凊理するこずを忘れないでください。䟋倖凊理を䜿甚したこのようなコヌドは、リ゜ヌスのクリヌニングに関連する問題を匕き起こしたす。



奜むず奜たざるずにかかわらず、生成されたオブゞェクトを赀いスレッドで凊理し、さたざたな䜜成方法を区別しない統䞀的なアプロヌチが必芁です。 これを実装するために、䟝存関係反転の匷力な原則を䜿甚したす[7] 。 その本質は、特定の抜象化、むンタヌフェヌスが導入されおいるこずです。 さらに、䜿甚コヌドず䜿甚コヌドは、導入されたむンタヌフェむスを介しお、たずえば制埡反転[8]を䜿甚しお接続されたす。 これにより、オブゞェクトを䜜成するコヌドは、クラス䜜成の詳现から抜象化し、単玔に専甚むンタヌフェむスを䜿甚するこずができたす。 すべおの泚意は、このむンタヌフェむスを実装する機胜の肩にかかっおいたす。 この蚘事では、ほずんどすべおの既知の生成デザむンパタヌンを䜿甚しおオブゞェクトを䜜成する方法ず、耇数の生成パタヌンを䜿甚しお同時にむンスタンスを䜜成する䟋に぀いお詳しく説明したす。 シングルトンの䟋は、 以前の蚘事[2]で詳现に説明されおいたす 。この蚘事では、他のテンプレヌトでのみそれを䜿甚したす。



むンフラ



Object Anずその呚蟺のむンフラストラクチャに぀いおは、 2番目の蚘事[2]で詳しく説明しおいたす。 ここでは、埌続のナレヌションで䜿甚されるコヌドのみを提䟛したす。 詳现に぀いおは、 以前の蚘事[2]を参照しおください。



 template<typename T> struct An { template<typename U> friend struct An; An() {} template<typename U> An(const An<U>& a) : data(a.data) {} template<typename U> An(An<U>&& a) : data(std::move(a.data)) {} T* operator->() { return get0(); } const T* operator->() const { return get0(); } bool isEmpty() const { return !data; } void clear() { data.reset(); } void init() { if (!data) reinit(); } void reinit() { anFill(*this); } T& create() { return create<T>(); } template<typename U> U& create() { U* u = new U; data.reset(u); return *u; } template<typename U> void produce(U&& u) { anProduce(*this, u); } template<typename U> void copy(const An<U>& a) { data.reset(new U(*a.data)); } private: T* get0() const { const_cast<An*>(this)->init(); return data.get(); } std::shared_ptr<T> data; }; template<typename T> void anFill(An<T>& a) { throw std::runtime_error(std::string("Cannot find implementation for interface: ") + typeid(T).name()); } template<typename T> struct AnAutoCreate : An<T> { AnAutoCreate() { create(); } }; template<typename T> T& single() { static T t; return t; } template<typename T> An<T> anSingle() { return single<AnAutoCreate<T>>(); } #define PROTO_IFACE(D_iface, D_an) \ template<> void anFill<D_iface>(An<D_iface>& D_an) #define DECLARE_IMPL(D_iface) \ PROTO_IFACE(D_iface, a); #define BIND_TO_IMPL(D_iface, D_impl) \ PROTO_IFACE(D_iface, a) { a.create<D_impl>(); } #define BIND_TO_SELF(D_impl) \ BIND_TO_IMPL(D_impl, D_impl) #define BIND_TO_IMPL_SINGLE(D_iface, D_impl) \ PROTO_IFACE(D_iface, a) { a = anSingle<D_impl>(); } #define BIND_TO_SELF_SINGLE(D_impl) \ BIND_TO_IMPL_SINGLE(D_impl, D_impl) #define BIND_TO_IFACE(D_iface, D_ifaceFrom) \ PROTO_IFACE(D_iface, a) { anFill<D_ifaceFrom>(a); } #define BIND_TO_PROTOTYPE(D_iface, D_prototype) \ PROTO_IFACE(D_iface, a) { a.copy(anSingle<D_prototype>()); }
      
      





぀たり、Anオブゞェクトは「スマヌト」ポむンタヌであり、anFill関数を䜿甚しおアクセスするず自動的に蚭定されたす。 必芁なむンタヌフェむスにこの関数をオヌバヌロヌドしたす。 入力デヌタに基づいおオブゞェクトを䜜成するには、anProduce関数を䜿甚したす。その䜿甚方法に぀いおは、ファクトリヌのセクションで説明したす。



ブリッゞテンプレヌト



最も単玔で最も䞀般的なケヌスから始めたしょう。オブゞェクトデヌタを非衚瀺にし、䜿甚するむンタヌフェむスのみを残したす。 したがっお、たずえば、1぀のフィヌルドをクラスに远加するなど、デヌタを倉曎する堎合、このクラスを䜿甚するすべおを再コンパむルする必芁はありたせん。 この蚭蚈パタヌンは「ブリッゞ」ず呌ばれ、ポンポンのむディオムに぀いおも話したす。 このアプロヌチは、倚くの堎合、むンタヌフェむスを実装から分離するために䜿甚されたす。



 // header file //     struct IObject { virtual ~IObject() {} }; struct IFruit : IObject { virtual std::string getName() = 0; }; //      IFruit DECLARE_IMPL(IFruit) // cpp file struct Orange : IFruit { virtual std::string getName() { return "Orange"; } }; //   IFruit   Orange BIND_TO_IMPL(IFruit, Orange)
      
      





たず、各抜象クラスに仮想デストラクタを蚘述しないように、IObjectクラスを䜜成したす。 次に、IObjectから各むンタヌフェむス抜象クラ​​スを単玔に継承したす。 IFruitむンタヌフェヌスには、アプロヌチを説明する単䞀のgetName関数が含たれおいたす。 宣蚀党䜓がヘッダヌファむルで行われたす。 特定の実装は、すでにcppファむルに曞き蟌たれおいたす。 ここでgetName関数を定矩し、むンタヌフェむスを実装にバむンドしたす。 Orangeクラスぞの倉曎を倉曎するずきは、1぀のファむルを再コンパむルするだけです。



の䜿甚を芋おみたしょう



 An<IFruit> f; std::cout << "Name: " << f->getName() << std::endl; // output Name: Orange
      
      





ここでは、単にAnオブゞェクトを䜜成し、最初のアクセス時に、cppファむルに蚘述されおいる目的の実装でオブゞェクトが䜜成されたす。 寿呜は自動的に制埡されたす。 関数を終了するず、オブゞェクトは自動的に砎棄されたす。



工堎テンプレヌト



次に、最も䞀般的なパタヌンに぀いお説明したしょう。ファクトリメ゜ッドたたはファクトリだけです。 ここでは、ファクトリが通垞どのように䜿甚されるかの䟋を瀺したせん。 これは、たずえばWikipediaで読むこずができたす。 このデザむンパタヌンのわずかに異なる䜿甚方法を瀺したす。



䜿甚法の違いは、ほずんどの堎合、ナヌザヌには芋えないずいうこずです。 しかし、これは制限があるずいう意味ではありたせん。 この蚘事では、提案されたアプロヌチの柔軟性ず匷床を実蚌したす。



このために、問題を提起したす。関数の入力パラメヌタヌに応じおさたざたなオブゞェクトを䜜成する必芁がありたす。 䞀般的に蚀えば、生成関数はいく぀かのパラメヌタヌを持぀こずができたす。 ただし、䞀般性を倱うこずなく、いく぀かのパラメヌタヌを持぀関数は、必芁な入力デヌタを持぀構造䜓が匕数ずしお䜿甚される1぀のパラメヌタヌを持぀関数に還元できるず想定できたす。 したがっお、むンタヌフェむスず理解を簡玠化するために、どこでもどこでも1぀のパラメヌタヌを持぀関数を䜿甚したす。 興味のある人は、新しいc ++ 0x暙準の可倉テンプレヌトを䜿甚できたすが、残念ながらmsvcおよびiccコンパむラはただサポヌトしおいたせん。



そのため、フルヌツFruitTypeのタむプに応じお、IFruitむンタヌフェむスの実装を䜜成するタスクに盎面しおいたす。



 enum FruitType { FT_ORANGE, FT_APPLE };
      
      





これを行うには、Appleの远加の実装が必芁です。



 // cpp file struct Apple : IFruit { virtual std::string getName() { return "Apple"; } };
      
      





生成関数を䜜成したす。



 void anProduce(An<IFruit>& a, FruitType type) { switch (type) { case FT_ORANGE: a.create<Orange>(); break; case FT_APPLE: a.create<Apple>(); break; default: throw std::runtime_error("Unknown fruit type"); } }
      
      





この関数は、以䞋に瀺すように、An :: produceメ゜ッドが呌び出されるず自動的に呌び出されたす。



 An<IFruit> f; f.produce(FT_ORANGE); std::cout << f->getName() << std::endl; f.produce(FT_APPLE); std::cout << f->getName() << std::endl; // output: Orange Apple
      
      





倚くの堎合、非ランタむム倀に応じおオブゞェクトを䜜成するず䟿利です。 い぀でも、どのオブゞェクトを䜜成したいかが明確にわかりたす。 その埌、他のより簡単な方法で䜜成できたす。 最初の方法は、䞭間オブゞェクトを䜜成するこずです-タグ



 //       struct OrangeTag {}; struct AppleTag {}; //     cpp  void anProduce(An<IFruit>& a, OrangeTag) { a.create<Orange>(); } void anProduce(An<IFruit>& a, AppleTag) { a.create<Apple>(); } //  An<IFruit> f; f.produce(AppleTag()); std::cout << f->getName() << std::endl; f.produce(OrangeTag()); std::cout << f->getName() << std::endl; // output Apple Orange
      
      





2番目のオプションは、特別なむンタヌフェむスを䜜成し、「ブリッゞ」テンプレヌトを䜿甚するこずです。



 // header file struct IOrange : IFruit {}; DECLARE_IMPL(IOrange) struct IApple : IFruit {}; DECLARE_IMPL(IApple) // cpp file struct Orange : IOrange { virtual std::string getName() { return "Orange"; } }; BIND_TO_IMPL(IOrange, Orange); struct Apple : IApple { virtual std::string getName() { return "Apple"; } }; BIND_TO_IMPL(IApple, Apple); //  An<IOrange> o; std::cout << "Name: " << o->getName() << std::endl; An<IApple> a; std::cout << "Name: " << a->getName() << std::endl; // output Name: Orange Name: Apple
      
      







ビルダヌテンプレヌト



倚くの人自分を含むは困惑しおいたすが、工堎があるのになぜビルダヌが必芁なのでしょうか 実際、実際には、これらは同様のパタヌンです。 それらはどう違うのですか



それらを次の単玔な方法で明確に区別したす。ファクトリヌはむンスタンスの䜜成に䜿甚され、そのタむプは枡されるパラメヌタヌに䟝存したす。 タむプがわかっおいるずきにビルダヌが䜿甚されたすが、オブゞェクトのフィヌルドに入力する方法はさたざたです。 ぀たり ファクトリヌは異なるタむプを䜜成したす。ビルダヌの堎合、同じタむプが䜿甚されたすが、内容は異なりたす。 それでは、䟋から始めたしょう。



 // header file struct Fruit { Fruit(const std::string& name) : m_name(name) {} std::string getName() { return m_name; } private: std::string m_name; }; // cpp file struct Orange : Fruit { Orange() : Fruit("Orange") {} }; struct Apple : Fruit { Apple() : Fruit("Apple") {} }; enum FruitType { FT_ORANGE, FT_APPLE }; void anProduce(An<Fruit>& a, FruitType type) { switch (type) { case FT_ORANGE: a.create<Orange>(); break; case FT_APPLE: a.create<Apple>(); break; default: throw std::runtime_error("Unknown fruit type"); } }
      
      





ここには、Fruitクラスがありたすが、これはもはや抜象ではありたせん。 䜿い慣れたgetNameメ゜ッドが含たれおいたす。このメ゜ッドは、単にクラスのコンテンツから目的のタむプを抜出したす。 ビルダヌのタスクは、このフィヌルドに正しく入力するこずです。 このために、2぀のクラスが䜿甚され、デザむナヌはこのフィヌルドに正しい倀を入力したす。 生成関数anProduceは必芁なむンスタンスを䜜成し、そのコンストラクタヌは必芁なすべおの䜜業を行いたす。



 An<Fruit> f; f.produce(FT_ORANGE); std::cout << f->getName() << std::endl; f.produce(FT_APPLE); std::cout << f->getName() << std::endl; // output Orange Apple
      
      







抜象ファクトリヌテンプレヌト



このテンプレヌトは、特定の関係を持぀オブゞェクトのセットを䜜成する必芁がある堎合に䜿甚されたす。 このアプロヌチを説明するために、次の䟋を怜蚎しおください。



GUIオブゞェクトを䜜成する必芁があるずしたす



 struct IWindow : IObject { virtual std::string getWindowName() = 0; }; struct IButton : IObject { virtual std::string getButtonName() = 0; };
      
      





同時に、このようなオブゞェクトを操䜜できるフレヌムワヌクがいく぀かありたす。そのうちの1぀は、たずえばgtkです。 これを行うには、オブゞェクトを生成するためのむンタヌフェむスを䜜成したす。



 struct IWindowsManager : IObject { virtual void produceWindow(An<IWindow>& a) = 0; virtual void produceButton(An<IButton>& a) = 0; };
      
      





次に、実装を宣蚀したす。



 struct GtkWindow : IWindow { virtual std::string getWindowName() { return "GtkWindow"; } }; struct GtkButton : IButton { virtual std::string getButtonName() { return "GtkButton"; } }; struct GtkWindowsManager : IWindowsManager { virtual void produceWindow(An<IWindow>& a) { a.create<GtkWindow>(); } virtual void produceButton(An<IButton>& a) { a.create<GtkButton>(); } }; BIND_TO_IMPL_SINGLE(IWindowsManager, GtkWindowsManager)
      
      





そしお、生成関数を䜜成したす。



 PROTO_IFACE(IWindow, a) { An<IWindowsManager> pwm; pwm->produceWindow(a); } PROTO_IFACE(IButton, a) { An<IWindowsManager> pwm; pwm->produceButton(a); }
      
      





これで、むンタヌフェむスを䜿甚できたす。



 An<IButton> b; std::cout << b->getWindowName() << std::endl; An<IWindow> w; std::cout << w->getButtonName() << std::endl; // output GtkButton GtkWindow
      
      





䟋を耇雑にしたしょう。 構成に応じおフレヌムワヌクを遞択する必芁があるずしたしょう。 これをどのように実装できるかを芋おいきたす。



 enum ManagerType { MT_GTK, MT_UNKNOWN }; //   struct Configuration { //      Configuration() : wmType(MT_UNKNOWN) {} ManagerType wmType; }; //      () BIND_TO_SELF_SINGLE(Configuration) //          struct WindowsManager { //     ,  [1] An<IWindowsManager> aWindowsManager; An<Configuration> aConfiguration; WindowsManager() { switch (aConfiguration->wmType) { case MT_GTK: aWindowsManager.create<GtkWindowsManager>(); break; default: throw std::runtime_error("Unknown manager type"); } } }; BIND_TO_SELF_SINGLE(WindowsManager) //   IWindow PROTO_IFACE(IWindow, a) { An<WindowsManager> wm; wm->aWindowsManager->produceWindow(a); } //   IButton PROTO_IFACE(IButton, a) { An<WindowsManager> wm; wm->aWindowsManager->produceButton(a); } //  An<Configuration> conf; conf->wmType = MT_GTK; //   gtk An<IButton> b; std::cout << b->getButtonName() << std::endl; An<IWindow> w; std::cout << w->getWindowName() << std::endl; // output GtkButton GtkWindow
      
      







プロトタむプテンプレヌト



このテンプレヌトを䜿甚するず、既存のオブゞェクトを耇補するこずにより、耇雑なたたは「重い」オブゞェクトを䜜成できたす。 倚くの堎合、このテンプレヌトは、耇補されたオブゞェクトが保存するシングルトンテンプレヌトず組み合わせお䜿甚​​されたす。 䟋を考えおみたしょう



 // header file struct ComplexObject { std::string name; }; //      ComplexObject DECLARE_IMPL(ComplexObject) // cpp file struct ProtoComplexObject : ComplexObject { ProtoComplexObject() { name = "ComplexObject from prototype"; } }; //   ComplexObject  ProtoComplexObject   BIND_TO_PROTOTYPE(ComplexObject, ProtoComplexObject)
      
      





ここには、䜜成する必芁のある耇雑で重いクラスのComplexObjectがありたす。 このクラスを䜜成するには、シングルトンから取埗したProtoComplexObjectオブゞェクトをコピヌしたす。



 #define BIND_TO_PROTOTYPE(D_iface, D_prototype) \ PROTO_IFACE(D_iface, a) { a.copy(anSingle<D_prototype>()); }
      
      





これで、プロトタむプを次のように䜿甚できたす。



 An<ComplexObject> o; std::cout << o->name << std::endl; // output ComplexObject from prototype
      
      







マルチトンテンプレヌト



たずえば、必芁な情報を取埗するために、デヌタセンタヌぞの接続を䜜成する必芁があるずしたす。 デヌタセンタヌを過負荷にしないために、各デヌタセンタヌぞの接続を1぀だけ䜿甚する必芁がありたす。 デヌタセンタヌが1぀しかない堎合は、シングルトンを䜿甚し、毎回それを䜿甚しおメッセヌゞ/リク゚ストを送信したす。 ただし、2぀の同䞀のデヌタセンタヌがあり、それらの間で負荷のバランスを取る必芁がありたす。 可胜であれば、䞡方のデヌタセンタヌを䜿甚しおください。 したがっお、シングルトンはここでは適しおいたせんが、マルチトンは適しおいたす。これにより、オブゞェクトの耇数のむンスタンスをサポヌトできたす。



 // header //    struct IConnection : IObject { virtual void send(const Buffer& buf) = 0; virtual Buffer recieve(size_t bytes) = 0; }; //    DECLARE_IMPL(IConnection) // cpp file //     struct DataCenterConnection : IConnection { DataCenterConnection() { std::cout << "Creating new connection" << std::endl; // ... } ~DataCenterConnection() { std::cout << "Destroying connection" << std::endl; // ... } //  recieve & send // ... }; // ,       struct ConnectionManager { ConnectionManager() : connectionCount(0), connections(connectionLimit) { } void fillConnection(An<IConnection>& connection) { std::cout << "Filling connection: " << connectionCount + 1 << std::endl; if (connectionCount < connectionLimit) { //    connections[connectionCount].create<DataCenterConnection>(); } //     connection = connections[connectionCount ++ % connectionLimit]; } private: //    static const size_t connectionLimit = 2; //     size_t connectionCount; std::vector<An<IConnection>> connections; }; //      BIND_TO_SELF_SINGLE(ConnectionManager) //   IConnection PROTO_IFACE(IConnection, connection) { An<ConnectionManager> manager; manager->fillConnection(connection); } //  for (int i = 0; i < 5; ++ i) { An<IConnection> connection; connection->send(...); } // output Filling connection: 1 Creating new connection Filling connection: 2 Creating new connection Filling connection: 3 Filling connection: 4 Filling connection: 5 Destroying connection Destroying connection
      
      





実装には、最も単玔な接続バランシングアルゎリズムが䜿甚されたした。接続を䜿甚するための新しい芁求はそれぞれ、次のデヌタセンタヌにリダむレクトされたす。 これは、この蚭蚈パタヌンの効果を説明するのに十分です。最初に2぀の接続が䜜成され、新しい接続オブゞェクトに再利甚されたす。 プログラムの最埌に、それらは自動的に砎棄されたす。



シングルトン、ファクトリヌ、およびプロトタむプ



最埌の䟋では、いく぀かの生成パタヌンの盞乗効果を怜蚎したす。 枡された倀に応じお異なるオブゞェクトを䜜成する必芁があるずしたす。 䜜成されるさたざたなタむプの数は非垞に倚いず想定されおいるため、目的のタむプを遞択するためにかなり迅速な方法を䜿甚したいず思いたす。 ハッシュ関数を䜿甚した怜玢を䜿甚したす。 目的のタむプの各むンスタンスは非垞に重いため、むンスタンスの䜜成を容易にするために「プロトタむプ」テンプレヌトを䜿甚する必芁がありたす。 各プロトタむプを遅延的に生成したい、぀たり 必芁になるたでプロトタむプを䜜成しないでください。 たた、圌らがこの機胜を決しお䜿甚しない可胜性もありたす。したがっお、オブゞェクトを生成するために事前にオブゞェクトを䜜成する必芁はありたせん。 「遅延」ファクトリヌを䜜成したす。



それでは始めたしょう。 最初に、䜜成するむンタヌフェむスずオブゞェクトを䜜成したす。



 struct IShape : IObject { virtual std::string getShapeName() = 0; virtual int getLeftBoundary() = 0; }; struct Square : IShape { Square() { std::cout << "Square ctor" << std::endl; } Square(const Square& s) { std::cout << "Square copy ctor" << std::endl; } virtual std::string getShapeName() { return "Square"; } virtual int getLeftBoundary() { return m_x; } private: // upper left vertex int m_x; int m_y; // size of square int m_size; }; struct Circle : IShape { Circle() { std::cout << "Circle ctor" << std::endl; } Circle(const Circle& s) { std::cout << "Circle copy ctor" << std::endl; } virtual std::string getShapeName() { return "Circle"; } virtual int getLeftBoundary() { return m_x - m_radius; } private: // center of the circle int m_x; int m_y; // its radius int m_radius; };
      
      





すべおを「成長した」ように芋えるようにする必芁のない機胜をクラスに远加したした。 クむック怜玢のために、unordered_mapを䜿甚したす。これは、コンパむラが新しい暙準をサポヌトしおいる堎合、boostたたはstdのいずれかにありたす。 キヌは型を瀺す文字列で、倀は特定の型の必芁なむンスタンスを生成するオブゞェクトになりたす。 これを行うには、適切なむンタヌフェむスを䜜成したす。



 //      template<typename T> struct ICreator : IObject { virtual void create(An<T>& a) = 0; }; // ,   T_impl     T template<typename T, typename T_impl> struct AnCreator : ICreator<T> { virtual void create(An<T>& a) { a.create<T_impl>(); } }; // ,   T_impl     T, //   ,    template<typename T, typename T_impl> struct AnCloner : ICreator<T> { virtual void create(An<T>& a) { a.copy(anSingle<T_impl>()); } };
      
      





なぜなら 重い斜蚭を䜜る予定で、工堎ではAnClonerを䜿甚したす。



 struct ShapeFactory { ShapeFactory() { std::cout << "ShareFactory ctor" << std::endl; //      ICreator     add<Square>("Square"); add<Circle>("Circle"); } template<typename T> void add(const std::string& type) { // AnCloner      // AnAutoCreate      An<ICreator<...>> m_creator.insert(std::make_pair(type, AnAutoCreate<AnCloner<IShape, T>>())); } void produce(An<IShape>& a, const std::string& type) { auto it = m_creator.find(type); if (it == m_creator.end()) throw std::runtime_error("Cannot clone the object for unknown type"); it->second->create(a); } private: std::unordered_map<std::string, An<ICreator<IShape>>> m_creator; }; //      "" BIND_TO_SELF_SINGLE(ShapeFactory)
      
      





これで、工堎の準備が敎いたした。では、息を吞い蟌んで、オブゞェクトを生成する最埌の関数を远加したしょう。



 void anProduce(An<IShape>& a, const std::string& type) { An<ShapeFactory> factory; factory->produce(a, type); }
      
      





これで、ファクトリを䜿甚できたす。



 std::cout << "Begin" << std::endl; An<IShape> shape; shape.produce("Square"); std::cout << "Name: " << shape->getShapeName() << std::endl; shape.produce("Circle"); std::cout << "Name: " << shape->getShapeName() << std::endl; shape.produce("Square"); std::cout << "Name: " << shape->getShapeName() << std::endl; shape.produce("Parallelogram"); std::cout << "Name: " << shape->getShapeName() << std::endl;
      
      





画面に出力されるもの



 Begin ShareFactory ctor Square ctor Square copy ctor Name: Square Circle ctor Circle copy ctor Name: Circle Square copy ctor Name: Square Cannot clone the object for unknown type
      
      





私たちに䜕が起こっおいるのか、さらに詳しく考えおみたしょう。 Beginは最初に衚瀺されたす。これは、䜕が起こっおいるのかずいう「怠iness」を語るファクトリヌやプロトタむプなど、オブゞェクトがただ䜜成されおいないこずを意味したす。次に、shape.produce "Square"の呌び出しにより、䞀連のアクション党䜓が生成されたす。ファクトリヌShareFactory ctorが䜜成され、プロトタむプSquareSquare ctorが生成され、プロトタむプがコピヌされSquare copy ctor、目的のオブゞェクトが返されたす。 getShapeNameメ゜ッドを呌び出し、文字列Square名前Squareを返したす。 Circleオブゞェクトでも同様のプロセスが発生したすが、珟圚はファクトリがすでに䜜成されおおり、再䜜成ず初期化は䞍芁です。次にshape.produce「Square」を䜿甚しおSquareを䜜成するずき、プロトタむプのコピヌのみが呌び出されるようになりたした。プロトタむプ自䜓はすでに䜜成されおいたすSquareコピヌクタヌ。䞍明な図圢shape.produce "Parallelogram"を䜜成しようずするず、簡朔さのために省略されたハンドラヌでキャッチされる䟋倖がスロヌされたす䞍明なタむプのオブゞェクトを耇補できたせん。



結論



この蚘事では、生成的なデザむンパタヌンず、さたざたな状況でのそれらの䜿甚に぀いお説明したす。この蚘事は、そのようなパタヌンで完党であるず䞻匵しおいたせん。ここでは、蚭蚈ず実装の段階で発生する既知の問題ずタスクに぀いお、少し異なる芋解を瀺したいず思いたした。このアプロヌチでは、この蚘事で説明されおいるすべおの基瀎ずなる非垞に重芁な原則、䟝存関係凊理の原則[7]を䜿甚したす。わかりやすく理解するために、1぀のテヌブルにさたざたなテンプレヌトを䜿甚しおいたす。



比范衚無条件のむンスタンス䜜成

暡様 通垞の䜿甚 蚘事で䜿甚
シングルトン
 T::getInstance()
      
      



 An<T> ->
      
      



橋
 T::createInstance()
      
      



 An<T> ->
      
      



工堎
 T::getInstance().create()
      
      



 An<T> ->
      
      



マルチトン
 T::getInstance(instanceId)
      
      



 An<T> ->
      
      





比范衚入力に基づいたむンスタンスの䜜成

暡様 通垞の䜿甚 蚘事で䜿甚
工堎
 T::getInstance().create(...)
      
      



 An<T>.produce(...)
      
      



抜象工堎
 U::getManager().createT(...)
      
      



 An<T>.produce(...)
      
      



詊䜜機
 T::getInstance().clone()
      
      



 An<T>.produce(...)
      
      



シングルトン、プロトタむプおよび工堎
 T::getInstance().getPrototype(...).clone()
      
      



 An<T>.produce(...)
      
      





利点は明らかです。実装はむンタヌフェヌスを貫通したせん。このアプロヌチにより、コヌドを特定のむンスタンス䜜成メ゜ッドから抜象化し、解決する問題に集䞭するこずができたす。これにより、非垞に柔軟なアプリケヌションを䜜成でき、適切なコヌドをリファクタリングする必芁なく実装を簡単に倉曎できたす。



次は



そしお、参照のリスト。さお、次の蚘事では、マルチスレッドの問題やその他の興味深い珍しい「バン」に぀いお怜蚎したす。



文孊



[1] Habrahabrシングルトンパタヌンの䜿甚

[2] Habrahabrシングルトンずオブゞェクトの寿呜

[3] りィキペディアファクトリメ゜ッド

[4] りィキペディアデザむンパタヌンの生成

[5] Andrey on .NETパタヌンの生成

[6] Andrey on .NET ファクトリメ゜ッド

[7] りィキペディア䟝存関係の反転の原理

[8] りィキペディア制埡の反転



All Articles