目录
11. 组合模式
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得客户端对单个对象和组合对象的使用具有一致性。
既然讲到以树形结构表示“部分-整体”,那可以将组合模式想象成一根大树,将大树分成树枝和树叶两部分,树枝上可以再长树枝,也可以长树叶,树叶上则不能再长出别的东西。
以下情况可以考虑使用组合模式:
1. 希望表示对象的部分-整体层次结构。
2. 希望客户端忽略组合对象与单个对象的不同,客户端将统一的使用组合结构中的所有对象。
/*
* 关键代码:树枝内部组合该接口,并且含有内部属性list,里面放Component。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;
//抽象类,提供组合和单个对象的一致接口
class Company
{
public:
Company(const string& name): m_name(name){}
virtual ~Company(){ cout << "~Company()" << endl;}
virtual void add(Company* ) = 0;
virtual void remove(const string&) = 0;
virtual void display(int depth) = 0;
virtual const string& name()
{
return m_name;
}
protected:
string m_name;
};
//具体的单个对象实现类,“树枝”类
class HeadCompany : public Company
{
public:
HeadCompany(const string& name): Company(name){}
virtual ~HeadCompany(){ cout << "~HeadCompany()" << endl;}
void add(Company* company) override
{
shared_ptr<Company> temp(company);
m_companyList.push_back(temp);
}
void remove(const string& strName) override
{
list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
for(; iter != m_companyList.end(); iter++)
{
if((*iter).get()->name() == strName)
{
//不应该在此处使用list<T>.erase(list<T>::iterator iter),会导致iter++错误,这里删除目标元素之后,必须return。
m_companyList.erase(iter);
return;
}
}
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << this->name().data() << endl;
list<shared_ptr<Company>>::iterator iter = m_companyList.begin();
for(; iter!= m_companyList.end(); iter++)
{
(*iter).get()->display(depth + 1);
}
}
private:
list<shared_ptr<Company>> m_companyList;
};
//具体的单个对象实现类,“树叶”类
class ResearchCompany : public Company
{
public:
ResearchCompany(const string& name): Company(name){}
virtual ~ResearchCompany(){ cout << "~ResearchCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
//具体的单个对象实现类,“树叶”类
class SalesCompany : public Company
{
public:
SalesCompany(const string& name): Company(name){}
virtual ~SalesCompany(){ cout << "~SalesCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
//具体的单个对象实现类,“树叶”类
class FinanceCompany : public Company
{
public:
FinanceCompany(const string& name): Company(name){}
virtual ~FinanceCompany(){ cout << "~FinanceCompany()" << endl;}
void add(Company* ) override
{
}
void remove(const string&) override
{
}
void display(int depth) override
{
for(int i = 0; i < depth; i++)
{
cout << "-";
}
cout << m_name.data() << endl;
}
};
int main()
{
HeadCompany* headRoot = new HeadCompany("Head Root Company");
HeadCompany* childRoot1 = new HeadCompany("Child Company A");
ResearchCompany* r1 = new ResearchCompany("Research Company A");
SalesCompany* s1 = new SalesCompany("Sales Company A");
SalesCompany* s2 = new SalesCompany("Sales Company B");
FinanceCompany* f1 = new FinanceCompany("FinanceCompany A");
childRoot1->add(r1);
childRoot1->add(s1);
childRoot1->add(s2);
childRoot1->add(f1);
HeadCompany* childRoot2 = new HeadCompany("Child Company B");
ResearchCompany* r2 = new ResearchCompany("Research Company B");
SalesCompany* s3 = new SalesCompany("Sales Company C");
SalesCompany* s4 = new SalesCompany("Sales Company D");
FinanceCompany* f2 = new FinanceCompany("FinanceCompany B");
childRoot2->add(r2);
childRoot2->add(s3);
childRoot2->add(s4);
childRoot2->add(f2);
headRoot->add(childRoot1);
headRoot->add(childRoot2);
headRoot->display(1);
cout << "\n***************\n" << endl;
childRoot1->remove("Sales Company B");
headRoot->display(1);
cout << "\n***************\n" << endl;
delete headRoot;
headRoot = nullptr;
return 0;
}
11.1 实例2
将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。注意两个字“树形”。这种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。
下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。
相应的代码实现为:
class Company
{
public:
Company(string name) { m_name = name; }
virtual ~Company(){}
virtual void Add(Company *pCom){}
virtual void Show(int depth) {}
protected:
string m_name;
};
//具体公司
class ConcreteCompany : public Company
{
public:
ConcreteCompany(string name): Company(name) {}
virtual ~ConcreteCompany() {}
void Add(Company *pCom) { m_listCompany.push_back(pCom); } //位于树的中间,可以增加子树
void Show(int depth)
{
for(int i = 0;i < depth; i++)
cout<<"-";
cout<<m_name<<endl;
list<Company *>::iterator iter=m_listCompany.begin();
for(; iter != m_listCompany.end(); iter++) //显示下层结点
(*iter)->Show(depth + 2);
}
private:
list<Company *> m_listCompany;
};
//具体的部门,财务部
class FinanceDepartment : public Company
{
public:
FinanceDepartment(string name):Company(name){}
virtual ~FinanceDepartment() {}
virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
{
for(int i = 0; i < depth; i++)
cout<<"-";
cout<<m_name<<endl;
}
};
//具体的部门,人力资源部
class HRDepartment :public Company
{
public:
HRDepartment(string name):Company(name){}
virtual ~HRDepartment() {}
virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
{
for(int i = 0; i < depth; i++)
cout<<"-";
cout<<m_name<<endl;
}
};
// 客户使用:
int main()
{
Company *root = new ConcreteCompany("总公司");
Company *leaf1=new FinanceDepartment("财务部");
Company *leaf2=new HRDepartment("人力资源部");
root->Add(leaf1);
root->Add(leaf2);
//分公司A
Company *mid1 = new ConcreteCompany("分公司A");
Company *leaf3=new FinanceDepartment("财务部");
Company *leaf4=new HRDepartment("人力资源部");
mid1->Add(leaf3);
mid1->Add(leaf4);
root->Add(mid1);
//分公司B
Company *mid2=new ConcreteCompany("分公司B");
FinanceDepartment *leaf5=new FinanceDepartment("财务部");
HRDepartment *leaf6=new HRDepartment("人力资源部");
mid2->Add(leaf5);
mid2->Add(leaf6);
root->Add(mid2);
root->Show(0);
delete leaf1; delete leaf2;
delete leaf3; delete leaf4;
delete leaf5; delete leaf6;
delete mid1; delete mid2;
delete root;
return 0;
}
上面的实现方式有缺点,就是内存的释放不好,需要客户自己动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所有的指针都是存在ConcreteCompany类的链表中。C++的麻烦,没有垃圾回收机制。
参考资料:https://blog.csdn.net/wuzhekai1985/article/details/6667564
参考资料:https://www.cnblogs.com/chengjundu/p/8473564.html
12. 代理模式
代理模式:为其它对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
优点:
1. 职责清晰。真实的角色只负责实现实际的业务逻辑,不用关心其它非本职责的事务,通过后期的代理完成具体的任务。这样代码会简洁清晰。
2. 代理对象可以在客户端和目标对象之间起到中介的作用,这样就保护了目标对象。
3. 扩展性好。
/*
* 关键代码:一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理
* 类再访问真正要访问的对象。
*/
#include <iostream>
using namespace std;
class Gril
{
public:
Gril(const string& name = "gril"):m_string(name){}
string getName()
{
return m_string;
}
private:
string m_string;
};
class Profession
{
public:
virtual ~Profession(){}
virtual void profess() = 0;
};
class YoungMan : public Profession
{
public:
YoungMan(const Gril& gril):m_gril(gril){}
void profess()
{
cout << "Young man love " << m_gril.getName().data() << endl;
}
private:
Gril m_gril;
};
class ManProxy : public Profession
{
public:
ManProxy(const Gril& gril):m_pMan(new YoungMan(gril)){}
~ManProxy()
{
delete m_pMan;
m_pMan = nullptr;
}
void profess()
{
m_pMan->profess();
}
private:
YoungMan* m_pMan;
};
int main(int argc, char *argv[])
{
Gril gril("heihei");
ManProxy* proxy = new ManProxy(gril);
proxy->profess();
delete proxy;
proxy = nullptr;
return 0;
}
12.1 实例2
为其他对象提供一种代理以控制对这个对象的访问。有四种常用的情况:(1)远程代理,(2)虚代理,(3)保护代理,(4)智能引用。本文主要介绍虚代理和智能引用两种情况。
考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。这是[DP]一书上的给的例子。下面给出代理模式的UML图。
简单实现如下:
class Image
{
public:
Image(string name): m_imageName(name) {}
virtual ~Image() {}
virtual void Show() {}
protected:
string m_imageName;
};
class BigImage: public Image
{
public:
BigImage(string name):Image(name) {}
~BigImage() {}
void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
};
class BigImageProxy: public Image
{
private:
BigImage *m_bigImage;
public:
BigImageProxy(string name):Image(name),m_bigImage(0) {}
~BigImageProxy() { delete m_bigImage; }
void Show()
{
if(m_bigImage == NULL)
m_bigImage = new BigImage(m_imageName);
m_bigImage->Show();
}
};
// 客户调用:
int main()
{
Image *image = new BigImageProxy("proxy.jpg"); //代理
image->Show(); //需要时由代理负责打开
delete image;
return 0;
}
在这个例子属于虚代理的情况,下面给两个智能引用的例子。一个是C++中的auto_ptr,另一个是smart_ptr。自己实现了一下。先给出auto_ptr的代码实现:
template<class T>
class auto_ptr {
public:
explicit auto_ptr(T *p = 0): pointee(p) {}
auto_ptr(auto_ptr<T>& rhs): pointee(rhs.release()) {}
~auto_ptr() { delete pointee; }
auto_ptr<T>& operator=(auto_ptr<T>& rhs)
{
if (this != &rhs) reset(rhs.release());
return *this;
}
T& operator*() const { return *pointee; }
T* operator->() const { return pointee; }
T* get() const { return pointee; }
T* release()
{
T *oldPointee = pointee;
pointee = 0;
return oldPointee;
}
void reset(T *p = 0)
{
if (pointee != p) {
delete pointee;
pointee = p;
}
}
private:
T *pointee;
};
阅读上面的代码,我们可以发现 auto_ptr 类就是一个代理,客户只需操作auto_prt的对象,而不需要与被代理的指针pointee打交道。auto_ptr 的好处在于为动态分配的对象提供异常安全。因为它用一个对象存储需要被自动释放的资源,然后依靠对象的析构函数来释放资源。这样客户就不需要关注资源的释放,由auto_ptr 对象自动完成。实现中的一个关键就是重载了解引用操作符和箭头操作符,从而使得auto_ptr的使用与真实指针类似。
我们知道C++中没有垃圾回收机制,可以通过智能指针来弥补,下面给出智能指针的一种实现,采用了引用计数的策略。
template <typename T>
class smart_ptr
{
public:
smart_ptr(T *p = 0): pointee(p), count(new size_t(1)) { } //初始的计数值为1
smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷贝构造函数,计数加1
~smart_ptr() { decr_count(); } //析构,计数减1,减到0时进行垃圾回收,即释放空间
smart_ptr& operator= (const smart_ptr& rhs) //重载赋值操作符
{
//给自身赋值也对,因为如果自身赋值,计数器先减1,再加1,并未发生改变
++*count;
decr_count();
pointee = rhs.pointee;
count = rhs.count;
return *this;
}
//重载箭头操作符和解引用操作符,未提供指针的检查
T *operator->() { return pointee; }
const T *operator->() const { return pointee; }
T &operator*() { return *pointee; }
const T &operator*() const { return *pointee; }
size_t get_refcount() { return *count; } //获得引用计数器值
private:
T *pointee; //实际指针,被代理
size_t *count; //引用计数器
void decr_count() //计数器减1
{
if(--*count == 0)
{
delete pointee;
delete count;
}
}
};
参考资料:https://blog.csdn.net/wuzhekai1985/article/details/6669219