c++设计模式-装饰器模式和代理模式

namespace _nmsp1
{
    //抽象的控件类
    class Control
    {
    public:
        virtual void draw() = 0; //draw方法,用于将自身绘制到屏幕上。
    public:
        virtual ~Control() {} //做父类时析构函数应该为虚函数
    };

    //列表控件类
    class ListCtrl :public Control
    {
    public:
        virtual void draw()
        {
            cout << "绘制普通的列表控件!" << endl; //具体可以用DirectX或OpenGL来绘制
        }
    };

    //抽象的装饰器类
    class Decorator :public Control
    {
    public:
        Decorator(Control* tmpctrl) :m_control(tmpctrl) {} //构造函数
        virtual void draw()
        {
            m_control->draw(); //虚函数,调用的是哪个draw,取决于m_control指向的对象
        }
    private:
        Control* m_control; //需要被装饰的其他控件,这里用的是Control *;
    };

    //----------------------
    //具体的“边框”装饰器类
    class BorderDec :public Decorator
    {
    public:
        BorderDec(Control* tmpctrl) :Decorator(tmpctrl) {} //构造函数
        virtual void draw()
        {
            Decorator::draw(); //调用父类的draw方法以保持以往已经绘制出的内容
            drawBorder(); //也要绘制自己的内容
        }
    private:
        void drawBorder()
        {
            cout << "绘制边框!" << endl;
        }
    };

    //具体的“垂直滚动条”装饰器类
    class VerScrollBarDec :public Decorator
    {
    public:
        VerScrollBarDec(Control* tmpctrl) :Decorator(tmpctrl) {} //构造函数
        virtual void draw()
        {
            Decorator::draw(); //调用父类的draw方法以保持以往已经绘制出的内容
            drawVerScrollBar(); //也要绘制自己的内容
        }
    private:
        void drawVerScrollBar()
        {
            cout << "绘制垂直滚动条!" << endl;
        }
    };

    //具体的“水平滚动条”装饰器类
    class HorScrollBarDec :public Decorator
    {
    public:
        HorScrollBarDec(Control* tmpctrl) :Decorator(tmpctrl) {} //构造函数
        virtual void draw()
        {
            Decorator::draw(); //调用父类的draw方法以保持以往已经绘制出的内容
            drawHorScrollBar(); //也要绘制自己的内容
        }
    private:
        void drawHorScrollBar()
        {
            cout << "绘制水平滚动条!" << endl;
        }
    };
}

int main()
{
    //(1)创建一个又带边框,又带垂直滚动条的列表控件
    //首先绘制普通的列表控件
    _nmsp1::Control* plistctrl = new _nmsp1::ListCtrl(); //本体

    //接着“借助普通的列表控件”,可以通过边框装饰器绘制出一个“带边框的列表控件”
    _nmsp1::Decorator* plistctrl_b = new _nmsp1::BorderDec(plistctrl); //_nmsp1::Decorator*用成_nmsp1::Control *

    //接着“借助带边框的列表控件”,就可以通过垂直滚动条装饰器绘制出一个“带垂直滚动条又带边框的列表控件”
    _nmsp1::Decorator* plistctrl_b_v = new _nmsp1::VerScrollBarDec(plistctrl_b);
    plistctrl_b_v->draw(); //这里完成最终绘制

    cout << "--------------------" << endl;
    //(2)创建一个只带水平滚动条的列表控件
    //首先绘制普通的列表控件
    _nmsp1::Control* plistctrl2 = new _nmsp1::ListCtrl(); //本体

    //接着“借助普通的列表控件”,可以通过水平滚动条装饰器绘制出一个“带水平滚动条的列表控件”
    _nmsp1::Decorator* plistctrl2_h = new _nmsp1::HorScrollBarDec(plistctrl2);
    plistctrl2_h->draw();

    //(3)释放资源
    delete plistctrl_b_v;
    delete plistctrl_b;
    delete plistctrl;

    delete plistctrl2_h;
    delete plistctrl2;


}
  

 

 

装饰(Decorator)模式:装饰器模式/包装模式,结构型模式

    //(1)问题的提出
     //UI(用户接口)界面。
       //a)ListCtrl类代表普通列表控件,提供draw方法
       //b)BorderListCtrl类,继承自ListCtrl,增加了边框的列表控件,提供draw方法
       //c)VerScBorderListCtrl类继承自BorderListCtrl,表示增加了边框又增加了垂直滚动条的列表控件,提供draw方法
       //d)HorScVerScBorderListCtrl类,继承自VerScBorderListCtrl,表示增加了边框,垂直、水平滚动条的列表控件,提供draw方法
    //继承 改为 组装方式来解决,防止类泛滥
       //a)ListCtrl类代表普通列表控件,提供draw方法
       //b)增加边框->带边框的列表控件
       //c)增加垂直滚动条->带纯质滚动条的列表控件,再给这个带垂直滚动条的列表控件增加一个水平滚动条->既带垂直滚动条又带水平滚动条的列表控件。

    //这种通过装饰方式将一个类的功能不断增强的思想(动态的增加新功能),就是装饰模式的核心设计思想。
    //public继承:is -a 关系,组合关系和委托关系。
 
(2)引入装饰(Decorator)模式
    //组合复用原则(Composite Reuse Principle,CRP),也称为合成复用原则/聚合复用原则。
      //若两个使用继承进行设计,则父类代码的修改可能影响子类的行为,而且,可能父类中的很多方法子类是用不上的,这显然是一种浪费,
       //若使用组合进行设计,则可以大大降低两个类之间的依赖关系,也不会存在因继承关系导致的浪费行为,所以
        //如果继承和组合都能达到设计目的,优先考虑使用组合(组合优于继承)。
    //“装饰”设计模式的定义(实现意图):动态的给一个对象添加一些额外的职责。就增加功能来说,该模式相比生成子类更加灵活。
    //装饰模式包含的四种角色:
     //a:Control(抽象构件):draw,让调用者以一致的方式处理未被修饰的对象以及经过修饰之后的对象,实现客户端的透明操作。
     //b:ListCtrl(具体构件):实现抽象构件定义的接口,此后,装饰器就可以给该构件增加额外的方法(职责);
     //c:Decorator(抽象装饰器类):
     //d:BorderDec、VerScrollBarDec、HorScrollBarDesc(具体装饰器类):增加了一些新方法,然后通过对draw接口的扩展,达到最终的修饰目的。
 
namespace _nmsp2
{
    //抽象饮料类
    class Beverage
    {
    public:
        virtual int getprice() = 0; //获取价格
    public:
        virtual ~Beverage() {}
    };

    //水果饮料类
    class FruitBeverage : public Beverage
    {
    public:
        virtual int getprice()
        {
            return 10; //一杯单纯的水果饮料,售价为10元
        }
    };

    //抽象的装饰器类
    class Decorator :public Beverage
    {
    public:
        Decorator(Beverage* tmpbvg) :m_pbvg(tmpbvg) {} //构造函数
        virtual int getprice()
        {
            return m_pbvg->getprice();
        }
    private:
        Beverage* m_pbvg;
    };

    //具体的“砂糖”装饰器类
    class SugarDec :public Decorator
    {
    public:
        SugarDec(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
        virtual int getprice()
        {
            return Decorator::getprice() + 1; //额外加多1元,要调用父类的getprice方法以把以往的价格增加进来
        }
    };

    //具体的“牛奶”装饰器类
    class MilkDesc :public Decorator
    {
    public:
        MilkDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
        virtual int getprice()
        {
            return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
        }
    };

    //具体的“珍珠”装饰器类
    class BubbleDesc :public Decorator
    {
    public:
        BubbleDesc(Beverage* tmpbvg) :Decorator(tmpbvg) {} //构造函数
        virtual int getprice()
        {
            return Decorator::getprice() + 2; //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
        }
    };
}
int main()
{
//创建一杯单纯的水果饮料,价格10元:
    _nmsp2::Beverage* pfruit = new _nmsp2::FruitBeverage();
    //向饮料中增加珍珠,价格多加了2元
    _nmsp2::Decorator *pfruit_addbubb = new _nmsp2::BubbleDesc(pfruit);
    //再向饮料中增加砂糖,价格又加多了1元
    _nmsp2::Decorator* pfruit_addbubb_addsugar = new _nmsp2::SugarDec(pfruit_addbubb);
    //输出最终的价格
    cout << "加了珍珠又加了砂糖的水果饮料最终价格是:" << pfruit_addbubb_addsugar->getprice() << "元人民币" << endl;

    //释放资源
    delete pfruit_addbubb_addsugar;
    delete pfruit_addbubb;
    delete pfruit;

    return 0;


}

 

代理模式

namespace _nmsp1
{
    class WebAddr
    {
    public:
        virtual void visit() = 0; //执行访问网站的动作,子类中会重新实现
        virtual ~WebAddr() {} //做父类时析构函数应该为虚函数
    };
    //某购物网站
    class WebAddr_Shopping :public WebAddr
    {
    public:
        virtual void visit()
        {
            //......访问该购物网站,可能涉及复杂的网络通信
            cout << "访问WebAddr_Shopping购物网站!" << endl;
        }
    };
    //某视频网站
    class WebAddr_Video :public WebAddr
    {
    public:
        virtual void visit()
        {
            //......访问该视频网站,可能涉及复杂的网络通信
            cout << "访问WebAddr_Video视频网站!" << endl;
        }
    };

    //-------------------------
    //网站代码类
    class WebAddrProxy :public WebAddr
    {
    public:
        //构造函数,引入的目的是传递进来要访问的具体网站
        WebAddrProxy(WebAddr* pwebaddr) :mp_webaddr(pwebaddr) {}

    public:
        virtual void visit()
        {
            //在这里进行访问的合法性检查,日志记录或者流量限制......
            mp_webaddr->visit();
            //在这里可以进行针对返回数据的过滤......
        }

    private:
        WebAddr* mp_webaddr; //代码要访问的具体网站
    };
    
    //-----------------
    //专门针对某购物网站WebAddr_Shopping的代理
    class WebAddr_Shopping_Proxy :public WebAddr
    {
    public:
        virtual void visit()
        {
            //在这里进行访问的合法性检查,日志记录或者流量限制......
            WebAddr_Shopping* p_webaddr = new WebAddr_Shopping();
            p_webaddr->visit();            
            //在这里可以进行针对返回数据的过滤......

            //释放资源
            delete p_webaddr;
        }
    };
}


int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
    

    _nmsp1::WebAddr* wba1 = new _nmsp1::WebAddr_Shopping();
    wba1->visit(); //访问该购物网站

    _nmsp1::WebAddr* wba2 = new _nmsp1::WebAddr_Video();
    wba2->visit(); //访问该视频网站

    //资源释放
    delete wba1;
    delete wba2;


    /*
    _nmsp1::WebAddr* wba1 = new _nmsp1::WebAddr_Shopping();
    _nmsp1::WebAddr* wba2 = new _nmsp1::WebAddr_Video();

    _nmsp1::WebAddrProxy* wbaproxy1 = new _nmsp1::WebAddrProxy(wba1);
    wbaproxy1->visit(); //通过代理去访问WebAddr_Shopping购物网站

    _nmsp1::WebAddrProxy* wbaproxy2 = new _nmsp1::WebAddrProxy(wba2);
    wbaproxy2->visit(); //通过代理去访问WebAddr_Video视频购物网站

    //资源释放
    delete wba1;
    delete wba2;
    //资源释放
    delete wbaproxy1;
    delete wbaproxy2;
    */

    /*
    _nmsp1::WebAddr_Shopping_Proxy* wbasproxy = new _nmsp1::WebAddr_Shopping_Proxy();
    wbasproxy->visit(); //访问实际的购物网站

    //资源释放
    delete wbasproxy;
    */
    
    return 0;
}
 代理(Proxy)模式:结构型模式
    //目的:为客户端增加额外的功能、约束或针对客户端的调用屏蔽一些复杂的细节问题。
    //(1)基本概念和范例
    //通过引入代理类来为原始类(被代理类)增加额外的能力——新功能、新服务,约束或者限制。
    //使用代理类的程序员与开发这个代理类的程序员并不是同一个程序员甚至可能是两个不同公司的程序员。

  (2)引入代理(Proxy)模式:
    //定义(实现意图):为其他对象提供一种代理以控制对这个对象的访问。
    //三种角色:
    //a)Subject(抽象主题),WebAddr类。
    //b)Proxy(代理主题):WebAddr_Shopping_Proxy类。
    //c)RealSubject(真实主题):WebAddr_Shopping
 

    //代理模式和装饰模式对比;
    //代理模式:代表真实主题并给真实主题增加一些新能力或者责任,这些功能与真实主题要实现的功能可能没有相关性,主要是控制对真实主题的访问。
    //装饰模式:新功能与原有构件能力具有相关性,是对原有构件能力或者行为的扩展(增强)。

  (3)代理模式的应用场合探究
    //( 3.1 )代理模式常用应用场景
    //常见的代理包括但不限于如下这些类型:
    //a)远程代理(Remote Proxy)
    //b)虚拟代理(Virtual Proxy)
    //c)保护代理(Protect Proxy)
    //d)缓存/缓冲代理(Cache Proxy)
    //e)智能引用代理(Smart Reference Proxy)。shared_ptr
    //f)写时复制(copy-on-write)优化代理:string
    //g)防火墙代理、同步代理,复杂隐藏代理等。
namespace _nmsp2
{
    //缓存/缓冲代理(Cache Proxy)范例
    vector<string> g_fileItemList; 

    //抽象主题
    class ReadInfo
    {
    public:
        virtual void read() = 0;
        virtual ~ReadInfo() {}
    };
    //真实主题
    class ReadInfoFromFile : public ReadInfo
    {
    public:
        virtual void read() //从文件中读信息(读取test.txt的内容)
        {
            ifstream fin("test.txt");
            if (!fin)
            {
                cout << "文件打开失败" << endl;
                return;
            }
            string linebuf;
            while (getline(fin, linebuf)) //从哪个文件中逐行读入内容
            {
                if (!linebuf.empty()) //读的不是空行
                {
                    g_fileItemList.push_back(linebuf); //将文件中的每一行都保存到vector容器中。
                    //cout << linebuf << endl;
                }
            }
            fin.close(); //关闭文件输入流
        }
    };
    //代理主题
    class ReadInfoProxy :public ReadInfo
    {
    public:
        virtual void read()
        {
            if (!m_loaded)
            {
                //没有从文件中载入信息,则载入
                m_loaded = true; //标记已经从文件中载入信息了
                cout << "从文件中读取了如下数据---------:" << endl;
                ReadInfoFromFile* rf = new ReadInfoFromFile();
                rf->read(); //将文件中的数据读入全局容器g_fileItemList
                delete rf; //释放资源
            }
            else
            {
                cout << "从缓存中读取了如下数据----------:" << endl;
            }
            //现在数据一定在g_fileItemList中,开始显示
            for (auto iter = g_fileItemList.begin(); iter != g_fileItemList.end(); ++iter)
            {
                cout << *iter << endl;
            }
        }
    private:
        bool m_loaded = false; //false表示还没有从文件中读出数据到内存
    };

}


int main()
{
    
    _nmsp2::ReadInfo* preadinfoproxy = new _nmsp2::ReadInfoProxy();
    preadinfoproxy->read(); //第一次调用read是借助代理使用真实主题到文件中拿数据
    preadinfoproxy->read(); //后续调用read都是直接从缓存中拿数据
    preadinfoproxy->read(); //从缓存中拿数据

    //资源释放
    delete preadinfoproxy;
    system("pause");
    return 0;


}

 

装饰器模式和代理模式在设计模式中有不同的应用场景和作用。

装饰器模式(Decorator Pattern)主要用于动态地给对象添加额外的职责。它允许向一个现有的对象添加新的功能,而无需改变其结构。装饰器模式通过创建一个装饰器类,该类实现了与被装饰对象相同的接口,并在内部持有一个被装饰对象的实例,从而可以动态地添加新的功能。这样就可以在运行时动态地添加、移除或者组合对象的行为,而不影响其原始行为。

代理模式(Proxy Pattern)则是用一个代理对象来控制对原始对象的访问。代理模式可以用于控制对对象的访问,以实现对原始对象的操作的增强、延迟加载或者权限控制。代理模式常见的应用包括远程代理、虚拟代理、保护代理等。代理对象通常会持有对真实对象的引用,并在对真实对象的访问上进行控制,例如在对真实对象的请求前后执行额外的逻辑。

因此,装饰器模式主要用于动态地扩展对象的功能,而代理模式主要用于控制对对象的访问。两种模式在应用场景和解决问题上有一定的差异,但都能够提供灵活性和可维护性。

 

当使用C++实现装饰器模式时,我们可以考虑一个简单的示例:假设我们有一个Shape接口和其具体实现类Circle,现在我们希望能够动态地给Circle添加额外的功能,比如颜色或者边框。这时候就可以使用装饰器模式。

首先,我们定义Shape接口及其具体实现类Circle

// Shape接口
class Shape {
public:
    virtual void draw() = 0;
};

// 具体的形状类:圆形
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "画一个圆形" << std::endl;
    }
};

然后,我们创建一个装饰器类ShapeDecorator,用于动态地添加额外的功能。这里以添加颜色为例:

// 装饰器类
class ShapeDecorator : public Shape {
protected:
    Shape* decoratedShape;

public:
    ShapeDecorator(Shape* shape) : decoratedShape(shape) {}

    void draw() override {
        decoratedShape->draw();
    }
};

// 具体的装饰器类:添加颜色
class ColorDecorator : public ShapeDecorator {
private:
    std::string color;

public:
    ColorDecorator(Shape* shape, const std::string& col) : ShapeDecorator(shape), color(col) {}

    void draw() override {
        decoratedShape->draw();
        std::cout << "颜色: " << color << std::endl;
    }
};

使用装饰器模式,我们可以这样来使用:

int main() {
    // 创建一个原始的圆形对象
    Shape* circle = new Circle();

    // 使用装饰器来动态地添加颜色
    Shape* redCircle = new ColorDecorator(circle, "红色");
    redCircle->draw();

    delete circle;
    delete redCircle;

    return 0;
}

在这个示例中,我们通过装饰器模式动态地给Circle对象添加了颜色的功能,而不需要修改Circle类的结构。

 
 

 

当使用C++实现装饰器模式时,我们可以考虑一个简单的示例:假设我们有一个Shape接口和其具体实现类Circle,现在我们希望能够动态地给Circle添加额外的功能,比如颜色或者边框。这时候就可以使用装饰器模式。

首先,我们定义Shape接口及其具体实现类Circle

// Shape接口
class Shape {
public:
    virtual void draw() = 0;
};

// 具体的形状类:圆形
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "画一个圆形" << std::endl;
    }
};

然后,我们创建一个装饰器类ShapeDecorator,用于动态地添加额外的功能。这里以添加颜色为例:

// 装饰器类
class ShapeDecorator : public Shape {
protected:
    Shape* decoratedShape;

public:
    ShapeDecorator(Shape* shape) : decoratedShape(shape) {}

    void draw() override {
        decoratedShape->draw();
    }
};

// 具体的装饰器类:添加颜色
class ColorDecorator : public ShapeDecorator {
private:
    std::string color;

public:
    ColorDecorator(Shape* shape, const std::string& col) : ShapeDecorator(shape), color(col) {}

    void draw() override {
        // 调用被装饰对象的draw方法
        decoratedShape->draw();
        // 添加额外的功能:颜色
        std::cout << "颜色: " << color << std::endl;
    }
};

使用装饰器模式,我们可以这样来使用:

int main() {
    // 创建一个原始的圆形对象
    Shape* circle = new Circle();

    // 使用装饰器来动态地添加颜色
    Shape* redCircle = new ColorDecorator(circle, "红色");
    redCircle->draw();

    delete circle;
    delete redCircle;

    return 0;
}

在这个示例中,我们通过装饰器模式动态地给Circle对象添加了颜色的功能,而不需要修改Circle类的结构。这样做的好处在于,可以在运行时动态地添加新的功能,而且还能保持类的单一职责原则,使得代码更加灵活和可维护。

 

当使用C++实现代理模式时,我们可以考虑一个简单的示例:假设我们有一个接口Image和其具体实现类RealImage,现在我们希望能够通过代理来控制对RealImage对象的访问。这时候就可以使用代理模式。

首先,我们定义Image接口及其具体实现类RealImage

// Image接口
class Image {
public:
    virtual void display() = 0;
};

// 具体的图像类:实际图像
class RealImage : public Image {
private:
    std::string filename;

public:
    RealImage(const std::string& file) : filename(file) {}

    void display() override {
        std::cout << "显示图像: " << filename << std::endl;
    }
};

然后,我们创建一个代理类ProxyImage,用于控制对RealImage对象的访问:

// 代理类
class ProxyImage : public Image {
private:
    std::string filename;
    RealImage* realImage;

    void loadImage() {
        if (realImage == nullptr) {
            realImage = new RealImage(filename);
        }
    }

public:
    ProxyImage(const std::string& file) : filename(file), realImage(nullptr) {}

    void display() override {
        loadImage();
        realImage->display();
    }
};

使用代理模式,我们可以这样来使用:

int main() {
    // 使用代理来控制对RealImage对象的访问
    Image* image = new ProxyImage("test.jpg");

    // 第一次访问,会加载真实图像
    image->display();

    // 第二次访问,不需要重新加载真实图像
    image->display();

    delete image;

    return 0;
}

在这个示例中,我们通过代理模式控制了对RealImage对象的访问。代理类ProxyImage负责在必要时创建并管理RealImage对象,并在适当的时机调用RealImage对象的方法。通过代理模式,我们可以实现延迟加载、访问控制等功能,而不需要直接访问RealImage对象。




posted @ 2024-05-27 21:03  白伟碧一些小心得  阅读(34)  评论(0编辑  收藏  举报