回顾享元模式,思考实作它的各种问题。
Prologue略过
FlyWeight Pattern实践享元模式,是将简单对象的雷同的组成元素抽出并独自保护的一种结构型设计模式。这些雷同的组成元素被称为共享元件,它们在一个独自的容器中被唯一性地治理,而简单对象只需持有到该惟一实例的参考,而无需反复创立这样的雷同的元素,从而可能大幅度地削减内存占用。
以字处理器为例,每个字符都具备独立的、区别于其它字符的非凡属性:例如字体款式,背景、边框、对齐等等。如果一个文档中全副字符都独自存储一份它的所有属性的正本,那么这将会是宏大的内存需要。但思考到一大堆(例如1000个)字符可能都有雷同的“宋体,9pt”这样的属性,那么实际上咱们只须要独自存储一份“宋体,9pt”的字体款式属性,而一个字符只须要一个指向该字体款式属性的指针就能够了,这就比1000个字符的1000个字体款式属性拷贝要节约的多。
相似的案例还有相当多,例如例子零碎中的每个粒子(例如子弹、弹片,或者敌方飞机)都有一些雷同的属性(例如色彩,轮廓等等)占地不小,但值却雷同。
工厂模式很容易想到,咱们能够在一个工厂中就地治理享元对象。当客户以具体值来申请一个享元对象时,工厂会从一个字典中检索享元是否存在,而后返回该元素的参考援用给客户。如果享元尚未存在,那么工厂会创立它,而后在返回援用。
不可变性依照传统的说法,享元模式要求这些雷同的局部(享元,雷同的组成元素)是不可变的。但这并不是铁律。
一个办法是,以一个享元为整体,咱们能够整体批改对象持有的享元参考。
例如咱们正在批改字处理器中的一个单词的字体款式,从“宋体,9pt”改为“黑体,12pt”,那么咱们能够间接批改援用指向。也就是说,咱们提供 character.apply_font_style(font_style& style) 这样的整体批改接口。
另一个办法能够从更细的粒度登程进行批改,例如从“宋体,9pt”改为“宋体,10pt”,但在产生变更时,尝试从工厂中查证新值的参考。也就是说,咱们提供 character.set_font_size(float pt) 这样的接口,但在其实现过程中记得去查证享元工厂(管理器)以求更新外部援用。
C++ 实现传统的享元模式的实现形式有这样的示例代码:
namespace hicc::dp::flyweight::basic { /** * flyweight Design Pattern * * Intent: Lets you fit more objects into the available amount of RAM by sharing * common parts of state between multiple objects, instead of keeping all of the * data in each object. */ struct shared_state { std::string brand_; std::string model_; std::string color_; shared_state(const std::string &brand, const std::string &model, const std::string &color) : brand_(brand) , model_(model) , color_(color) { } friend std::ostream &operator<<(std::ostream &os, const shared_state &ss) { return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]"; } }; struct unique_state { std::string owner_; std::string plates_; unique_state(const std::string &owner, const std::string &plates) : owner_(owner) , plates_(plates) { } friend std::ostream &operator<<(std::ostream &os, const unique_state &us) { return os << "[ " << us.owner_ << " , " << us.plates_ << " ]"; } }; /** * The flyweight stores a common portion of the state (also called intrinsic * state) that belongs to multiple real business entities. The flyweight accepts * the rest of the state (extrinsic state, unique for each entity) via its * method parameters. */ class flyweight { private: shared_state *shared_state_; public: flyweight(const shared_state *o) : shared_state_(new struct shared_state(*o)) { } flyweight(const flyweight &o) : shared_state_(new struct shared_state(*o.shared_state_)) { } ~flyweight() { delete shared_state_; } shared_state *state() const { return shared_state_; } void Operation(const unique_state &unique_state) const { std::cout << "flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n"; } }; /** * The flyweight Factory creates and manages the flyweight objects. It ensures * that flyweights are shared correctly. When the client requests a flyweight, * the factory either returns an existing instance or creates a new one, if it * doesn't exist yet. */ class flyweight_factory { std::unordered_map<std::string, flyweight> flyweights_; std::string key(const shared_state &ss) const { return ss.brand_ + "_" + ss.model_ + "_" + ss.color_; } public: flyweight_factory(std::initializer_list<shared_state> lists) { for (const shared_state &ss : lists) { this->flyweights_.insert(std::make_pair<std::string, flyweight>(this->key(ss), flyweight(&ss))); } } /** * Returns an existing flyweight with a given state or creates a new one. */ flyweight get(const shared_state &shared_state) { std::string key = this->key(shared_state); if (this->flyweights_.find(key) == this->flyweights_.end()) { std::cout << "flyweight_factory: Can't find a flyweight, creating new one.\n"; this->flyweights_.insert(std::make_pair(key, flyweight(&shared_state))); } else { std::cout << "flyweight_factory: Reusing existing flyweight.\n"; } return this->flyweights_.at(key); } void list() const { size_t count = this->flyweights_.size(); std::cout << "\nflyweight_factory: I have " << count << " flyweights:\n"; for (std::pair<std::string, flyweight> pair : this->flyweights_) { std::cout << pair.first << "\n"; } } }; // ... void AddCarToPoliceDatabase( flyweight_factory &ff, const std::string &plates, const std::string &owner, const std::string &brand, const std::string &model, const std::string &color) { std::cout << "\nClient: Adding a car to database.\n"; const flyweight &flyweight = ff.get({brand, model, color}); // The client code either stores or calculates extrinsic state and passes it // to the flyweight's methods. flyweight.Operation({owner, plates}); }} // namespace hicc::dp::flyweight::basicvoid test_flyweight_basic() { using namespace hicc::dp::flyweight::basic; flyweight_factory *factory = new flyweight_factory({ {"Chevrolet", "Camaro2018", "pink"}, {"Mercedes Benz", "C300", "black"}, {"Mercedes Benz", "C500", "red"}, {"BMW", "M5", "red"}, {"BMW", "X6", "white"} }); factory->list(); AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "M5", "red"); AddCarToPoliceDatabase(*factory, "CL234IR", "James Doe", "BMW", "X1", "red"); factory->list(); delete factory;}其输入后果如同这样:
...