C++设计模式整理008-组合模式和代理模式

目录

11. 组合模式

11.1 实例2

12. 代理模式

12.1 实例2


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

参考资料:https://www.cnblogs.com/chengjundu/p/8473564.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公众号:程序喵星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值