/ janturon.cz / Od kodéra k analytikovi / Kompozit a Visitor

Kompozit a Visitor

Kompozit

Kompozit je uspořádání objektů do (většinou stromové) struktury, kde se nachází uzly (adresáře) a listy (položky), umožňující volání téže metody (např. zobrazení názvu) na všech prvcích. K tomu se většinou používá rozhraní podobné tomuto:

class Composit { public: string name; char type; public: virtual string view(string path="") =0; };

Z něj lze zdědit adresáře i položky:

Položka class Item : public Composit { public: Item(string name) { this->name = name; } string view(string path="") { return path+name+"\n"; } }; Adresář class Folder : public Composit { vector items; public: Folder(string name) { this->name = name; } void insert(Composit* c) { items.push_back(c); } string view(string path="") { path+= name; if(items.size()==0) return path+"\n"; path+= "/"; string result = ""; for(unsigned int i=0; i<items.size(); i++) { result+= items[i]->view(path); } return result; } };

Potom můžeme vytvořit a zobrazit adresářovou strukturu, např.:

int main() { Folder *root=new Folder("root"), *folder1=new Folder("folder1"), *folder2=new Folder("folder2"); Item *item0=new Item("item0"), *item1=new Item("item1"), *item2=new Item("item2"); root->insert(folder1); root->insert(folder2); root->insert(item0); folder1->insert(item1); folder1->insert(folder2); folder2->insert(item2); cout << root->view() << endl; }

a výstupem je:

root/folder1/item1 root/folder1/folder2/item2 root/folder2/item2 root/item0

Visitor

Tento návrhový vzor umožňuje zavolat objektu (často kompozitu) metodu z jiné třídy i pro své privátní členy. Doplníme pouze metodu accept(), která toto navštěvování umožní. Upravme příklad vlevo na:

class Composit { public: string name; char type; public: virtual string view(string path="") =0; virtual void accept(Visitor* v) { v->visit(this); } }; Adresář class Folder : public Composit { vector items; public: Folder(string name) { this->name = name; } void insert(Composit* c) { items.push_back(c); } string view(string path="") { path+= name; if(items.size()==0) return path+"\n"; path+= "/"; string result = ""; for(unsigned int i=0; i<items.size(); i++) { result+= items[i]->view(path); } return result; } void accept(Visitor* v) { for(unsigned int i=0; i<items.size(); i++) { items[i]->accept(visitor); } visitor->visit(this); } };

Rozhraní Visitor má metodu visit

class Visitor { public: virtual void visit(Composit*) =0; };

To může implementovat třeba napsat třídu, která provede označení vybraného adresáře a všech členů pod ním:

class Highlighter : public Visitor { public: void visit(Composit* tgt) { tgt->name = "*"+tgt->name; } void highlight(Composit* tgt) { tgt->accept(this); } };

Pak můžeme psát

int main() { Folder *root=new Folder("root"), *folder1=new Folder("folder1"), *folder2=new Folder("folder2"); Item *item0=new Item("item0"), *item1=new Item("item1"), *item2=new Item("item2"); Highlighter *highlighter=new Highlighter(); root->insert(folder1); root->insert(folder2); root->insert(item0); folder1->insert(item1); folder1->insert(folder2); folder2->insert(item2); highlighter->highlight(folder2); cout << root->view() << endl; }

a výstupem je:

root/folder1/item1 root/folder1/*folder2/*item2 root/*folder2/*item2 root/item0