Compositeパターンをshared_ptrで実装するの続き。
DeepCopyを作る”Clone”メソッドや、他のComponentから内容をコピーする”Assign”メソッドもあると便利。
Compositeもメンバ変数を保持することも想定して実装してみる。
もっとスマートなやり方もありそうだが、とりあえず、Compositeとその子クラスに非公開のデフォルトコンストラクタを実装する方式で。
(前回同様、c++11の機能を使っているが、c++11以前でもboost利用で同様の実装が可能。また行数省略のためすべてheaderに実装している。)
1.Componentクラス
Clone/Assign用の純粋仮想関数を追加する。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Component { ・・・・・・(省略)・・・・・・・ //------------------------------------ // Clone / Assign: //____________________________________ public: virtual std::shared_ptr<Component> Clone()const = 0; virtual void Assign( const std::shared_ptr<const Component>& src ) = 0; }; |
2.Compositeクラス
Clone/Assignメソッドを実装する。
Compositeの派生クラス用の純粋仮想関数doClone/doAssignも追加。
デフォルトConstructorはprotectedとする。
メンバ変数”name_”も追加、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
#include <vector> #include <algorithm> #include <string> //--------------------------------------------------------------------------- #include "Component.h" //--------------------------------------------------------------------------- class Composite : public Component,public std::enable_shared_from_this<Composite> { //-------------------------------- // Member and Accessor: //________________________________ private: std::vector<std::shared_ptr<Component>> childs_; std::string name_; //-------------------------------- // Constructor: //________________________________ public: explicit Composite( const std::string& name ) : Component(), childs_(), name_(name) {} protected: // Clone/Assign用のコンストラクタ。公開の必要はなし。子クラスのみからアクセスできればOK Composite() : Component(), childs_(), name_() {} //-------------------------------- // Override Method: //________________________________ public: virtual void Add( const std::shared_ptr<Component>& compo ) { if( std::find( childs_.begin(), childs_.end(), compo ) == childs_.end() ) { childs_.push_back( compo ); compo->setParent( shared_from_this() ); } } virtual void Remove( const std::shared_ptr<Component>& compo ) { childs_.erase( remove( childs_.begin(), childs_.end(), compo ), childs_.end() ); } // virtual void Execute( int depth ) { for( int i = 0; i < depth; ++i ) std::cout << " "; std::cout << "name:" << name_ << "/"; doExecute(); for( size_t i = 0; i < childs_.size(); ++i ) { childs_[i]->Execute( depth + 1 ); } } //------------------------------------ // Clone / Assign: //____________________________________ protected virtual std::shared_ptr<Component> doClone()const = 0; virtual bool doAssign( const Component* const src ) = 0; public: virtual std::shared_ptr<Component> Clone()const { std::shared_ptr<Component> clone = doClone(); clone->Assign( shared_from_this() ); return clone; } virtual void Assign( const std::shared_ptr<const Component>& src ) { if( doAssign( src.get() ) ) { std::shared_ptr<const Composite> obj = std::dynamic_pointer_cast<const Composite>( src ); // 子クラスの処理 childs_.clear(); for( size_t i = 0; i < obj->childs_.size(); ++i ) { Add( obj->childs_[i]->Clone() ); } // Compositeクラスのメンバの処理 name_ = obj->name_; } } }; |
3.ConcreteCompositeクラス
doClone/doAssignメソッドを実装する。非公開のコンストラクタも追加。
doCloneは非公開のデフォルトコンストラクタで作成したshared_ptrを返すだけ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include "Composite.h" //--------------------------------------------------------------------------- class ConcreteCompositeA : public Composite { //-------------------------------- // Member and Accessor: //________________________________ private: int val_; //-------------------------------- // Constructor: //________________________________ public: ConcreteCompositeA( const std::string& name, int val ) : Composite(name), val_(val) {} private: // doClone用のコンストラクタ。公開の必要なし ConcreteCompositeA() : Composite(), val_() {} //-------------------------------- // Destructor: //________________________________ public: ~ConcreteCompositeA(){} //-------------------------------- // Override Method: //________________________________ public: virtual void doExecute() { std::cout << "A: val == " << val_ << std::endl; } //------------------------------------ // Clone / Assign: //____________________________________ protected: virtual std::shared_ptr<Component> doClone()const { return std::shared_ptr<Component>( new ConcreteCompositeA() ); } virtual bool doAssign( const Component* const src ) { const ConcreteCompositeA* from = dynamic_cast<const ConcreteCompositeA*>( src ); if( from == nullptr ) return false; // このクラスの値を代入 val_ = from->val_; return true; } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include "Composite.h" //--------------------------------------------------------------------------- class ConcreteCompositeB : public Composite { //-------------------------------- // Member and Accessor: //________________________________ private: std::string str_; public: // ---- set ---- // ---- get ---- //-------------------------------- // Constructor: //________________________________ public: ConcreteCompositeB( const std::string& name, const std::string& str ) : Composite(name), str_(str) {} private: // doClone用のコンストラクタ。公開の必要なし ConcreteCompositeB() : Composite(), str_() {} //-------------------------------- // Destructor: //________________________________ public: ~ConcreteCompositeB(){} //-------------------------------- // Override Method: //________________________________ public: virtual void doExecute() { std::cout << "B: str == " << str_ << std::endl; } //------------------------------------ // Clone / Assign: //____________________________________ protected: virtual std::shared_ptr<Component> doClone()const { return std::shared_ptr<Component>( new ConcreteCompositeB() ); } virtual bool doAssign( const Component* const src ) { const ConcreteCompositeB* from = dynamic_cast<const ConcreteCompositeB*>( src ); if( from == nullptr ) return false; // このクラスの値を代入 str_ = from->str_; return true; } }; |
(不正なAssignの対応は何もしていないが、本来は例外を投げるなどのエラー処理が必要かも)
4.テストコード
適当に親子関係を作成した後、CloneとAssignを実行する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <stdio.h> #include <conio.h> #include "ConcreteCompositeA.h" #include "ConcreteCompositeB.h" int _tmain(int argc, _TCHAR* argv[]) { std::shared_ptr<Component> c1 = std::make_shared<ConcreteCompositeA>( "c1", 3 ); std::shared_ptr<Component> c2 = std::make_shared<ConcreteCompositeB>( "c2", "child" ); std::shared_ptr<Component> c3 = std::make_shared<ConcreteCompositeB>( "c3", "child of child" ); std::shared_ptr<Component> c4 = std::make_shared<ConcreteCompositeA>( "c4", 107 ); std::shared_ptr<Component> c5 = std::make_shared<ConcreteCompositeB>( "c5", "other child" ); c3->Add( c4 ); c2->Add( c3 ); c1->Add( c2 ); c1->Add( c5 ); c1->Execute( 0 ); // Clone: std::cout << "--Clone--" << std::endl; std::shared_ptr<Component> cloned_compo = c1->Clone(); cloned_compo->Execute( 0 ); // Assign: std::cout << "--Before Assign--" << std::endl; std::shared_ptr<Component> assigned_compo = std::make_shared<ConcreteCompositeA>( "assigned_compo", 0 ); assigned_compo->Execute( 0 ); std::cout << "--After Assign--" << std::endl; assigned_compo->Assign( c1 ); assigned_compo->Execute( 0 ); getch(); return 0; } |
実行結果
————————
name:c1/A: val == 3
name:c2/B: str == child
name:c3/B: str == child of child
name:c4/A: val == 107
name:c5/B: str == other child
–Clone–
name:c1/A: val == 3
name:c2/B: str == child
name:c3/B: str == child of child
name:c4/A: val == 107
name:c5/B: str == other child
–Before Assign–
name:assigned_compo/A: val == 0
–After Assign–
name:c1/A: val == 3
name:c2/B: str == child
name:c3/B: str == child of child
name:c4/A: val == 107
name:c5/B: str == other child
————————
ConcreteCompositeのメンバを追加・変更した場合、doAssignメソッドのみを書き換えればOK。