本周的设计模式比较多了,主要分为“对象性能”模式,“状态变化”模式,
对象性能模式
Singleton单件模式
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
class Singleton{
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance=nullptr;
//线程非安全版本
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
//线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
//双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance() {
if(m_instance==nullptr){
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;
Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
在单件模式中,为了让多个线程同时运行时,保证只存在一个类对象实例,分别使用了线程安全但代价很高的锁、为了改进性能设计的有线程不安全因素的双检查锁、以及C++后的跨平台线程安全双检查锁。
Flyweight享元模式
Flyweight模式主要为了使大量细粒度的对象同时存在系统中时带来的巨大的开销。如果这些细粒度的对象存在共同的数据成员或者变量,那么可以通过共享的方式来减小数据的重复带来的内存开销以及CPU开销。
比如,如果设计了一个字符串带有自己的字体,那么就可以把所有字符串所共有的字体独立共享出来,达到减小内存开销的目的。
class Font {
private:
//unique object key
string key;
//object state
//....
public:
Font(const string& key){
//...
}
};
class FontFactory{
private:
map<string,Font* > fontPool;
public:
Font* GetFont(const string& key){
map<string,Font*>::iterator item=fontPool.find(key);
if(item!=footPool.end()){
return fontPool[key];
}
else{
Font* font = new Font(key);
fontPool[key]= font;
return font;
}
}
void clear(){
//...
}
};
状态变化模式
在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。
State状态模式
允许一个对象在其内部状态改变时,改变它的行为。从而使对象看起来似乎修改了其行为。
要实现State状态模式,实际上依然是通过虚函数来实现的。通过定义一个state基类,让state基类指针指向初始state子类对象,当状态改变时,也就是该指针指向了另外一个state子类对象时,虚函数所执行的效果便不同了。
Memento备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
在备忘录模式中,通过Memento去获取原发器的状态,并进行序列化的存储,在需要的时候,通过原发器的成员函数来恢复原发器到某一种状态上去。
数据结构模式
常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用。这时候,将这些特定数据结构封装在内部,在外部提供统一接口,来实现与数据结构无关的访问,是一种行之有效的解决方案。
Composite模式
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。
#include <iostream>
#include <list>
#include <string>
#include <algorithm>
using namespace std;
class Component
{
public:
virtual void process() = 0;
virtual ~Component(){}
};
//树节点
class Composite : public Component{
string name;
list<Component*> elements;
public:
Composite(const string & s) : name(s) {}
void add(Component* element) {
elements.push_back(element);
}
void remove(Component* element){
elements.remove(element);
}
void process(){
//1. process current node
//2. process leaf nodes
for (auto &e : elements)
e->process(); //多态调用
}
};
//叶子节点
class Leaf : public Component{
string name;
public:
Leaf(string s) : name(s) {}
void process(){
//process current node
}
};
void Invoke(Component & c){
//...
c.process();
//...
}
int main()
{
Composite root("root");
Composite treeNode1("treeNode1");
Composite treeNode2("treeNode2");
Composite treeNode3("treeNode3");
Composite treeNode4("treeNode4");
Leaf leat1("left1");
Leaf leat2("left2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1);
root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2);
process(root);
process(leaf2);
process(treeNode3);
}
Composite模式到现在都还在广泛使用,因为它将接口与实际对象的内部结构隔离开来,从而使得客户程序对接口的使用具有一致性(稳定)。
Iterator迭代器模式
Chain of Responsibility职责链
行为变化
Command
Visitor
领域规则模式
在特定领域中,某些变化虽然频繁,但可以抽象为某种规则。这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案。
Iterpreter
在经典Iterpreter模式中,通过将+和-操作用规则抽象为表达式的类,从而可以处理任意不带优先级的+和-组合操作。