|
| 1 | + |
| 2 | +#include <iostream> |
| 3 | +#include <string> |
| 4 | +#include <list> |
| 5 | +#include <algorithm> |
| 6 | +/** |
| 7 | + * EN: Composite Design Pattern |
| 8 | + * |
| 9 | + * Intent: Lets you compose objects into tree structures and then work with |
| 10 | + * these structures as if they were individual objects. |
| 11 | + * |
| 12 | + * RU: Паттерн Компоновщик |
| 13 | + * |
| 14 | + * Назначение: Позволяет сгруппировать объекты в древовидную структуру, а затем |
| 15 | + * работать с ними так, как будто это единичный объект. |
| 16 | + */ |
| 17 | +/** |
| 18 | + * EN: The base Component class declares common operations for both simple and |
| 19 | + * complex objects of a composition. |
| 20 | + * |
| 21 | + * RU: Базовый класс Компонент объявляет общие операции как для простых, так и |
| 22 | + * для сложных объектов структуры. |
| 23 | + */ |
| 24 | +class Component |
| 25 | +{ |
| 26 | + /** |
| 27 | + * @var Component |
| 28 | + */ |
| 29 | +protected: |
| 30 | + Component *parent_; |
| 31 | + /** |
| 32 | + * EN: Optionally, the base Component can declare an interface for setting |
| 33 | + * and accessing a parent of the component in a tree structure. It can also |
| 34 | + * provide some default implementation for these methods. |
| 35 | + * |
| 36 | + * RU: При необходимости базовый Компонент может объявить интерфейс для |
| 37 | + * установки и получения родителя компонента в древовидной структуре. Он |
| 38 | + * также может предоставить некоторую реализацию по умолчанию для этих |
| 39 | + * методов. |
| 40 | + */ |
| 41 | +public: |
| 42 | + virtual ~Component() {} |
| 43 | + void SetParent(Component *parent) |
| 44 | + { |
| 45 | + this->parent_ = parent; |
| 46 | + } |
| 47 | + Component *GetParent() const |
| 48 | + { |
| 49 | + return this->parent_; |
| 50 | + } |
| 51 | + /** |
| 52 | + * EN: In some cases, it would be beneficial to define the child-management |
| 53 | + * operations right in the base Component class. This way, you won't need to |
| 54 | + * expose any concrete component classes to the client code, even during the |
| 55 | + * object tree assembly. The downside is that these methods will be empty |
| 56 | + * for the leaf-level components. |
| 57 | + * |
| 58 | + * RU: В некоторых случаях целесообразно определить операции управления |
| 59 | + * потомками прямо в базовом классе Компонент. Таким образом, вам не нужно |
| 60 | + * будет предоставлять конкретные классы компонентов клиентскому коду, даже |
| 61 | + * во время сборки дерева объектов. Недостаток такого подхода в том, что эти |
| 62 | + * методы будут пустыми для компонентов уровня листа. |
| 63 | + */ |
| 64 | + virtual void Add(Component *component) {} |
| 65 | + virtual void Remove(Component *component) {} |
| 66 | + /** |
| 67 | + * EN: You can provide a method that lets the client code figure out whether |
| 68 | + * a component can bear children. |
| 69 | + * |
| 70 | + * RU: Вы можете предоставить метод, который позволит клиентскому коду |
| 71 | + * понять, может ли компонент иметь вложенные объекты. |
| 72 | + */ |
| 73 | + virtual bool IsComposite() const |
| 74 | + { |
| 75 | + return false; |
| 76 | + } |
| 77 | + /** |
| 78 | + * EN: The base Component may implement some default behavior or leave it to |
| 79 | + * concrete classes (by declaring the method containing the behavior as |
| 80 | + * "abstract"). |
| 81 | + * |
| 82 | + * RU: Базовый Компонент может сам реализовать некоторое поведение по |
| 83 | + * умолчанию или поручить это конкретным классам, объявив метод, содержащий |
| 84 | + * поведение абстрактным. |
| 85 | + */ |
| 86 | + virtual std::string Operation() const = 0; |
| 87 | +}; |
| 88 | +/** |
| 89 | + * EN: The Leaf class represents the end objects of a composition. A leaf can't |
| 90 | + * have any children. |
| 91 | + * |
| 92 | + * Usually, it's the Leaf objects that do the actual work, whereas Composite |
| 93 | + * objects only delegate to their sub-components. |
| 94 | + * |
| 95 | + * RU: Класс Лист представляет собой конечные объекты структуры. Лист не может |
| 96 | + * иметь вложенных компонентов. |
| 97 | + * |
| 98 | + * Обычно объекты Листьев выполняют фактическую работу, тогда как объекты |
| 99 | + * Контейнера лишь делегируют работу своим подкомпонентам. |
| 100 | + */ |
| 101 | +class Leaf : public Component |
| 102 | +{ |
| 103 | +public: |
| 104 | + std::string Operation() const override |
| 105 | + { |
| 106 | + return "Leaf"; |
| 107 | + } |
| 108 | +}; |
| 109 | +/** |
| 110 | + * EN: The Composite class represents the complex components that may have |
| 111 | + * children. Usually, the Composite objects delegate the actual work to their |
| 112 | + * children and then "sum-up" the result. |
| 113 | + * |
| 114 | + * RU: Класс Контейнер содержит сложные компоненты, которые могут иметь |
| 115 | + * вложенные компоненты. Обычно объекты Контейнеры делегируют фактическую работу |
| 116 | + * своим детям, а затем «суммируют» результат. |
| 117 | + */ |
| 118 | +class Composite : public Component |
| 119 | +{ |
| 120 | + /** |
| 121 | + * @var \SplObjectStorage |
| 122 | + */ |
| 123 | +protected: |
| 124 | + std::list<Component *> children_; |
| 125 | + |
| 126 | +public: |
| 127 | + /** |
| 128 | + * EN: A composite object can add or remove other components (both simple or |
| 129 | + * complex) to or from its child list. |
| 130 | + * |
| 131 | + * RU: Объект контейнера может как добавлять компоненты в свой список |
| 132 | + * вложенных компонентов, так и удалять их, как простые, так и сложные. |
| 133 | + */ |
| 134 | + void Add(Component *component) override |
| 135 | + { |
| 136 | + this->children_.push_back(component); |
| 137 | + component->SetParent(this); |
| 138 | + } |
| 139 | + /** |
| 140 | + * EN: Have in mind that this method removes the pointer to the list but doesn't frees the |
| 141 | + * memory, you should do it manually or better use smart pointers. |
| 142 | + * |
| 143 | + * RU: |
| 144 | + */ |
| 145 | + void Remove(Component *component) override |
| 146 | + { |
| 147 | + |
| 148 | + children_.remove(component); |
| 149 | + component->SetParent(nullptr); |
| 150 | + } |
| 151 | + bool IsComposite() const override |
| 152 | + { |
| 153 | + return true; |
| 154 | + } |
| 155 | + /** |
| 156 | + * EN: The Composite executes its primary logic in a particular way. It |
| 157 | + * traverses recursively through all its children, collecting and summing |
| 158 | + * their results. Since the composite's children pass these calls to their |
| 159 | + * children and so forth, the whole object tree is traversed as a result. |
| 160 | + * |
| 161 | + * RU: Контейнер выполняет свою основную логику особым образом. Он проходит |
| 162 | + * рекурсивно через всех своих детей, собирая и суммируя их результаты. |
| 163 | + * Поскольку потомки контейнера передают эти вызовы своим потомкам и так |
| 164 | + * далее, в результате обходится всё дерево объектов. |
| 165 | + */ |
| 166 | + std::string Operation() const override |
| 167 | + { |
| 168 | + std::string result; |
| 169 | + for (const Component *c : children_) |
| 170 | + { |
| 171 | + if (c == children_.back()) |
| 172 | + { |
| 173 | + result += c->Operation(); |
| 174 | + } |
| 175 | + else |
| 176 | + { |
| 177 | + result += c->Operation() + "+"; |
| 178 | + } |
| 179 | + } |
| 180 | + return "Branch(" + result + ")"; |
| 181 | + } |
| 182 | +}; |
| 183 | +/** |
| 184 | + * EN: The client code works with all of the components via the base interface. |
| 185 | + * |
| 186 | + * RU: Клиентский код работает со всеми компонентами через базовый интерфейс. |
| 187 | + */ |
| 188 | +void ClientCode(Component *component) |
| 189 | +{ |
| 190 | + // ... |
| 191 | + std::cout << "RESULT: " << component->Operation(); |
| 192 | + // ... |
| 193 | +} |
| 194 | + |
| 195 | +/** |
| 196 | + * EN: Thanks to the fact that the child-management operations are declared in |
| 197 | + * the base Component class, the client code can work with any component, simple |
| 198 | + * or complex, without depending on their concrete classes. |
| 199 | + * |
| 200 | + * RU: Благодаря тому, что операции управления потомками объявлены в базовом |
| 201 | + * классе Компонента, клиентский код может работать как с простыми, так и со |
| 202 | + * сложными компонентами, вне зависимости от их конкретных классов. |
| 203 | + */ |
| 204 | +void ClientCode2(Component *component1, Component *component2) |
| 205 | +{ |
| 206 | + // ... |
| 207 | + if (component1->IsComposite()) |
| 208 | + { |
| 209 | + component1->Add(component2); |
| 210 | + } |
| 211 | + std::cout << "RESULT: " << component1->Operation(); |
| 212 | + // ... |
| 213 | +} |
| 214 | + |
| 215 | +/** |
| 216 | + * EN: This way the client code can support the simple leaf components... |
| 217 | + * |
| 218 | + * RU: Таким образом, клиентский код может поддерживать простые |
| 219 | + * компоненты-листья... |
| 220 | + */ |
| 221 | + |
| 222 | +int main() |
| 223 | +{ |
| 224 | + Component *simple = new Leaf; |
| 225 | + std::cout << "Client: I've got a simple component:\n"; |
| 226 | + ClientCode(simple); |
| 227 | + std::cout << "\n\n"; |
| 228 | + /** |
| 229 | + * EN: ...as well as the complex composites. |
| 230 | + * |
| 231 | + * RU: ...а также сложные контейнеры. |
| 232 | + */ |
| 233 | + |
| 234 | + Component *tree = new Composite; |
| 235 | + Component *branch1 = new Composite; |
| 236 | + |
| 237 | + Component *leaf_1 = new Leaf; |
| 238 | + Component *leaf_2 = new Leaf; |
| 239 | + Component *leaf_3 = new Leaf; |
| 240 | + branch1->Add(leaf_1); |
| 241 | + branch1->Add(leaf_2); |
| 242 | + Component *branch2 = new Composite; |
| 243 | + branch2->Add(leaf_3); |
| 244 | + tree->Add(branch1); |
| 245 | + tree->Add(branch2); |
| 246 | + std::cout << "Client: Now I've got a composite tree:\n"; |
| 247 | + ClientCode(tree); |
| 248 | + std::cout << "\n\n"; |
| 249 | + |
| 250 | + std::cout << "Client: I don't need to check the components classes even when managing the tree:\n"; |
| 251 | + ClientCode2(tree, simple); |
| 252 | + std::cout << "\n"; |
| 253 | + |
| 254 | + delete simple; |
| 255 | + delete tree; |
| 256 | + delete branch1; |
| 257 | + delete branch2; |
| 258 | + delete leaf_1; |
| 259 | + delete leaf_2; |
| 260 | + delete leaf_3; |
| 261 | + |
| 262 | + return 0; |
| 263 | +} |
0 commit comments