zoukankan      html  css  js  c++  java
  • 第11章 结构型模式—装饰模式

    1. 装饰模式(Decorator Pattern)的定义

    (1)动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。

      ①装饰模式是为对象而不是类添加功能的。

      ②用组合方式,而不是继承方式为对象添加功能。

    (2)装饰模式的结构和说明

     

      ①Component:组件对象的接口,可以给这些对象动态地添加职责。

      ②ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰器装饰的原始对象,也就是可以给这个对象添加职责。

      ③Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象的指针,其实就是持有一个被装饰的对象。之所以从Decorator继承而来,除了与被装饰对象接口相同,还是有个原因是Decorator本身也可以被进一步的装饰,形成多层的装饰。注意,装饰后仍然是一个Component对象,而且其功能更为复杂。

      ④ConcreteDecorator:实际的装饰器对象,实现具体要向被装饰对象添加的功能。

    (3)思考装饰模式

      ①装饰模式的本质动态组。动态是手段,组合才是目的。通过对象组合(而不是继承)来实现为被装饰对象透明的增加功能,而且也可以控制功能的访问。

      ②装饰模式的动机:由于继承为类型引入的静态特质,使得通过继承扩展方式缺乏灵活性,并且随着子类的增多,会导致子类的膨胀。而装饰模式可以根据需要来动态地使“对象功能扩展”,避免子类膨胀问题

      ③装饰器:不仅仅可以给被装饰对象增加功能,还可以根据需要选择是否调用被装饰对象的功能。如果不调用,那就变成完全重新实现,相当于动态修改了被装饰对象的功能。同时装饰器一定要实现和组件类一致的接口,保证它们是同一个类型,并具有同一个外观,这样组合完成的装饰才能够递归调用下去

    【编程实验】奖金的计算

    //结构型模式:装饰模式
    //场景:计算奖金的方法。
    //奖金的计算方式:
    //1.奖金分为个人奖金和业务主管(经理)两种不同的计算方式等。
    //2.个人奖金大致有个人当月业务奖金、个人累计奖金、个人业务增长奖金、及时回款奖金
    //  限时成交加码奖等。对于业务主管除了个人奖金外,还有团队累计奖金和团队业务增长
    //  奖金、团队盈利奖等。
    
    //简化后的奖金计算体系
    //1.每个人当月业务奖金 = 当月销售额* 3%
    //2.每个人累计奖金 = 总的回款额* 0.1%
    //3.团队奖金 = 团队总销售额* 1%
    #include <iostream>
    #include <map>
    #include <string>
    
    using namespace std;
    //********************辅助类*********************************
    //在内存中模拟数据库,准备测试数据,好计算奖金
    class TempDB
    {
    private:
        TempDB(){}
    public:
        //记录每个人的月度销售额,只用了人员,月份没有用
        static map<string, double>  mapSalary;  
    };
    
    static map<string, double>::value_type init_value[]=
    {
        map<string, double>::value_type("ZhangSan", 10000.0),
        map<string, double>::value_type("LiSi",     20000.0),
        map<string, double>::value_type("WangWu",   30000.0)
    };
    
    map<string, double>  TempDB::mapSalary(init_value, init_value + 3);
    
    //************************抽象组件类******************
    //计算奖金的组件接口
    class Component
    {
    public:
       virtual double calcPrize(string user, int beginDate, int endDate) = 0; 
    };
    
    //基本的实现计算奖金的类,也是被装饰器装饰的对象
    class ConcreteComponent: public Component
    {
    public:
        double calcPrize(string user, int beginDate, int endDate)
        {
            return 0; //只是一个默认的实现(没有奖金)
        }
    };
    
    //************************定义抽象的装饰器**********************
    class Decorator : public Component
    {
    protected:
        Component* component;
    public:
        Decorator(Component* component){this->component = component;}
        
        double calcPrize(string user, int beginDate, int endDate)
        {
            //转调组件对象的方法
            return component->calcPrize(user, beginDate, endDate);
        }
    };
    
    //定义一系列的装饰器对象
    //装饰器对象:计算当月业务奖金
    class MonthPrizeDecorator: public Decorator
    {
    public:
        MonthPrizeDecorator(Component* component):Decorator(component){} 
        double calcPrize(string user, int beginDate, int endDate)
        {
            //1.先获取前面运算出来的奖金
            double money = Decorator::calcPrize(user, beginDate, endDate);
            
            //2.然后计算当月业务奖金,按人员和时间去获取当月业务额,然后*3%
            double prize = TempDB::mapSalary[user]*0.03;
            
            cout << user << " MonPrize: " << prize << endl;
            
            return money + prize;
        }    
    };
    
    //装饰器对象:计算累计奖金
    class SumPrizeDecorator: public Decorator
    {
    public:
        SumPrizeDecorator(Component* component):Decorator(component){} 
        double calcPrize(string user, int beginDate, int endDate)
        {
            //1.先获取前面运算出来的奖金
            double money = Decorator::calcPrize(user, beginDate, endDate);
            
            //2.然后计算累计奖金,按人员和时间去获取当月业务额,然后*0.1%
            //简单演示一下,假定大家的累计业务额都是100000元
            double prize = 100000*0.001;
            
            cout << user << " SumPrize: " << prize << endl;
            
            return money + prize;
        }    
    };
    
    //装饰器对象:计算当月团队业务奖金
    class GroupPrizeDecorator: public Decorator
    {
    public:
        GroupPrizeDecorator(Component* component):Decorator(component){} 
        double calcPrize(string user, int beginDate, int endDate)
        {
            //1.先获取前面运算出来的奖金
            double money = Decorator::calcPrize(user, beginDate, endDate);
            
            //2.然后计算当月团队业务奖金,先计算出团队总的业务额,然后*1%
            //假设都是一个团队的
            double group = 0.0;
            map<string, double>::iterator iter =TempDB::mapSalary.begin();
            
            while(iter != TempDB::mapSalary.end())
            {
                group += iter->second;
                ++iter;
            }
            
            double prize = group* 0.01;
            
            cout << user << " GroupPrize: " << prize << endl;
            
            return money + prize;
        }    
    };
    
    int main()
    { 
        //客户端调用
    
        //先创建计算基本奖金的类,这也是被装饰的对象
        Component* c1 = new ConcreteComponent();
        
        //然后对计算的基本奖金进行装饰,这里要组合各个装饰
        //说明,各个装饰者之间最好是不要有先后顺序的限制
        
        //先组合普通业务人员的奖金计算
        Decorator* d1 = new MonthPrizeDecorator(c1);
        Decorator* d2 = new SumPrizeDecorator(d1);
        
        //注意:这里只需要使用最后组合好的对象调用业务方法即可,会依次调用
        //各个装饰者。日期对象没有用上,所0就可以了。
        double zs = d2->calcPrize("ZhangSan", 0, 0);
        cout <<"ZhangSan, totalMoney: " << zs << endl;
        
        cout << endl;
        
        double ls = d2->calcPrize("LiSi", 0, 0);
        cout <<"LiSi, totalMoney: " << ls << endl;
    
        cout << endl;
        //如果是业务经理,还需要一个计算团队的奖金
        Decorator* d3 = new GroupPrizeDecorator(d2);
        double ww = d3->calcPrize("WangWu", 0, 0);
        cout <<"WangWu, totalMoney: " << ww << endl;
        
        delete c1;
        delete d1;
        delete d2;
        delete d3;
        
        return 0;
    }

    2. 装饰模式的优缺点

    (1)优点

      ①比继承更灵活:继承是静态的,而装饰模式把功能分离到每个装饰器,然后通过组合方式,在运行时动态地组合功能。

      ②更容易复用功能:一般一个装饰器只实现一个功能,使实现装饰器变得简单,更重要的是这样有利于装饰器功能的复用,可以给一个对象增加多个不同装饰器,也可以用一个装饰器不同的对象,从而实现复用装饰器的功能。

      ③简化高层定义:通过组合装饰器的方式,在进行高层定义的时候,不用把所有功能都定义出来,而是定义最基本的就可以了,在需要的时候,组合相应的装饰器来完成所需的功能。

    (2)缺点

      ①会产生很多细粒度的对象

      ②多层的装饰是比较复杂的,应尽量减少装饰类的数据,以便降低系统的复杂度。

    3. 使用场景

    (1)在不影响其他对象的情况下,以动态、透明的方式给对象添加职责。

    (2)不适合使用子类来进行扩展时,可以考虑使用装饰模式。因为装饰模式是使用“对象组合”的方式。所谓不适合用子类扩展的方式,比如扩展功能需要的子类太多,造成子类数目呈爆炸性增长。

    (3)需要为一批兄弟类进行改装或加装功能。可以选用装饰模式。

    【编程实验】在显示每个Window前追加显示某个logo的功能。

    //结构型模式:装饰模式
    //场景:在显示每个Window前追加显示某个logo的功能。
    
    #include <iostream>
    
    using namespace std;
    
    //************************抽象组件类******************
    class BaseWin  //Component
    {
    public:
       virtual void show() = 0; //显示
    };
    
    //***********************具体组件角色*******************
    //PrintDialog
    class PrintDialog : public BaseWin  
    {
    public:   
        void show()
        {
            cout << "PrintDialog" << endl;
        }   
    };
    
    //WinConfig:配置窗口
    class WinConfig : public BaseWin  
    {
    public:   
        void show()
        {
            cout << "WinConfig" << endl;
        }   
    };
    
    //MainWin:主窗口
    class MainWin : public BaseWin  
    {
    public:   
        void show()
        {
            cout << "MainWin" << endl;
        }   
    };
    
    //*****************************装饰器类**********************
    class DecoratorWin : public BaseWin //继承,如此装饰后仍是BaseWin类
    {
    private:
        BaseWin* mWin; //持有一个被装饰对象的指针
    public:
        DecoratorWin(BaseWin* Win){this->mWin = Win;}
        void show()
        {
            mWin->show();
        }
    };
    
    //具体的装饰器
    class LogWin : public DecoratorWin
    {
    private:
        void displayLogo() {cout << "LogoWin Showing..." << endl;}
        
    public:
        LogWin(BaseWin* win) : DecoratorWin(win){}
    
        void show()
        {
            displayLogo(); //增加功能,在原窗口显示之前,先显示Logo窗口
            DecoratorWin::show();
        }    
    };
    
    
    int main()
    { 
        //客户端调用
        
        //显示主窗口前先显示Logo窗口
        BaseWin* mainWin= new MainWin();
        BaseWin* logo = new LogWin(mainWin);
        logo->show();
    
        //显示打印对话框前先显示Logo窗口
        BaseWin* printDlg = new PrintDialog();
        BaseWin* logo2 = new LogWin(printDlg);
        logo2->show();
         
        delete mainWin;
        delete logo;
        delete printDlg;
        delete logo2;
     
        return 0;
    }

    4. 相关模式

    (1)装饰模式与策略模式

      ①策略模式也可以实现动态地改变对象的功能,但是策略模式只是一层选择,也就是根据策略选择一下具体的实现类而己。而装饰模式不是一层,而是递归调用,无数层都可以。

      ②策略模式改变的是原始对象的功能,其改变的是对象的内核。而装饰器可以看作是一个对象的外壳,改变的是经过前一个装饰器装饰后的对象,可改变对象的行为。

      ③Decorator模式仅从外部改变对象,因此对象无需对它的装饰有任何了解;也就是装饰对象该对象来说是透明的。但策略模式,Component组件本身知道可能进行哪些扩充,因此它必须引用并维护相应的策略。

      ④策略的方法可能需要修改Component组件以适应新的扩充,另一方面,一个策略可以有自己特定的接口,而装饰的接口则必须与组件接口一致。

    (2)装饰模式与模板方法模式

      两者的功能有点相似,模板方法主要应用在算法骨架固定的情况。如果是一个相对动态的算法,可以使用装饰模式,因为用装饰器组装时,其实也相当于一个调用算法的步骤,相当于一个动态的算法骨架。

  • 相关阅读:
    PHP 输出true false
    code::blocks 注释快捷键
    GDAL 网址
    wine qq2011安装
    C++ ACM解题
    C++内存分配秘籍—new,malloc,GlobalAlloc详解(Zhuan)
    grub4dos初级教程-入门篇(Z)
    GDAL 编译(转)
    ubuntu双系统安装
    shapfile格式说明(转)
  • 原文地址:https://www.cnblogs.com/5iedu/p/5538182.html
Copyright © 2011-2022 走看看