zoukankan      html  css  js  c++  java
  • 设计模式---单一职责模式之装饰模式(Decorator)

    前提:"单一职责"模式

    在软件组件的设计中,如果责任划分的不清晰,使用继承,得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任

    典型模式(表现最为突出)

    装饰模式Decorator
    桥接模式Bridge

     一:装饰模式

    (一)概念

    装饰模式又叫做包装模式。通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一个替换方案。
    装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择的,顺序的使用装饰功能包装对象

    (二)动机

    在某些情况下我们可能会“过度的使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;
    并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多的子类膨胀。
    即我们需要找到一种方式,实现功能可通过非继承方式扩展,但是接口不发生变化的类。
    如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?
    从而使得任何“功能扩展变化”所导致的影响将为最低?

    (三)原代码讲解(流操作) 

    文件流,内存流,网络流,有对流进行加密,缓存等操作
    //业务操作
    class Stream{  //公共基类,含有共有方法
    publicvirtual char Read(int number)=0;
        virtual void Seek(int position)=0;
        virtual void Write(char data)=0;
        
        virtual ~Stream(){}
    };
    
    //主体类:文件流,网络流,内存流三个类
    class FileStream: public Stream{
    public:
        virtual char Read(int number){
            //读文件流
        }
        virtual void Seek(int position){
            //定位文件流
        }
        virtual void Write(char data){
            //写文件流
        }
    
    };
    
    class NetworkStream :public Stream{
    public:
        virtual char Read(int number){
            //读网络流
        }
        virtual void Seek(int position){
            //定位网络流
        }
        virtual void Write(char data){
            //写网络流
        }
        
    };
    
    class MemoryStream :public Stream{
    public:
        virtual char Read(int number){
            //读内存流
        }
        virtual void Seek(int position){
            //定位内存流
        }
        virtual void Write(char data){
            //写内存流
        }
        
    };
    
    //扩展操作,对各个流进行操作,加密
    class CryptoFileStream :public FileStream{
    public:
        virtual char Read(int number){
           
            //额外的加密操作...
            FileStream::Read(number);//读文件流  静态特质,定死了,永远都是文件操作
            
        }
        virtual void Seek(int position){
            //额外的加密操作...
            FileStream::Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            FileStream::Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    class CryptoNetworkStream : :public NetworkStream{
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            NetworkStream::Read(number);//读网络流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            NetworkStream::Seek(position);//定位网络流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            NetworkStream::Write(data);//写网络流
            //额外的加密操作...
        }
    };
    
    class CryptoMemoryStream : public MemoryStream{
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            MemoryStream::Read(number);//读内存流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            MemoryStream::Seek(position);//定位内存流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            MemoryStream::Write(data);//写内存流
            //额外的加密操作...
        }
    };
    
    //扩展操作,对各个流进行操作,缓存操作
    class BufferedFileStream : public FileStream{ //... }; class BufferedNetworkStream : public NetworkStream{ //... }; class BufferedMemoryStream : public MemoryStream{ //... } //扩展操作,对各个流进行操作,加密和缓存组合操作,这里只写了一个,实际上有多种组合 class CryptoBufferedFileStream :public FileStream{ public: virtual char Read(int number){ //额外的加密操作... //额外的缓冲操作... FileStream::Read(number);//读文件流 } virtual void Seek(int position){ //额外的加密操作... //额外的缓冲操作... FileStream::Seek(position);//定位文件流 //额外的加密操作... //额外的缓冲操作... } virtual void Write(byte data){ //额外的加密操作... //额外的缓冲操作... FileStream::Write(data);//写文件流 //额外的加密操作... //额外的缓冲操作... } }; void Process(){ //编译时装配 CryptoFileStream *fs1 = new CryptoFileStream(); BufferedFileStream *fs2 = new BufferedFileStream(); CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream(); }

    出现的问题:

    需要的类的个数是:
                1+n+n*(m+m-1+...+2+1)        
    其中n是我们流的个数,m是我们的操作类型个数。
    我们上面的n=3,m=2所以需要的类是13个

    问题的原因

    除了其中的具体操作,例如:文件读取,网络读取等不同,我们发现所有的加密和缓存操作都是一样的方法,出现大量代码冗余,

    (四)改进版本一(组合代替继承

    //扩展操作
    class CryptoFileStream{
        FileStream* stream;
    public:
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
            
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    class CryptoNetworkStream{
        NetworkStream* stream;
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            stream->Read(number);//读网络流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位网络流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写网络流
            //额外的加密操作...
        }
    };
    
    class CryptoMemoryStream{
        NetworkStream* stream;
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            stream->Read(number);//读内存流  
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位内存流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写内存流
            //额外的加密操作...
        }
    };
    当一个变量的声明类型都是某个类的子类的时候,我们就该将他声明为某个类(基类),由于多态,我们可以使得他在未来(运行时)成为子类
    //扩展操作
    class CryptoFileStream{
        Stream* stream;  //使用基类,消除了编译时依赖
    public:
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
            
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    class CryptoNetworkStream{
        Stream* stream;
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            stream->Read(number);//读网络流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位网络流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写网络流
            //额外的加密操作...
        }
    };
    
    class CryptoMemoryStream{
        Stream* stream;
    public:
        virtual char Read(int number){
            
            //额外的加密操作...
            stream->Read(number);//读内存流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位内存流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写内存流
            //额外的加密操作...
        }
    };
    我们发现这3个类一样,除了类名之外都相同,所以我们可以进行合并,消除重复性
    //扩展操作
    class CryptoFileStream{
        Stream* stream;
    public:
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
            
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    
    class BufferedFileStream{  //所有相同操作都将去重,只保留一个
    //...
    };
    同时发现一个问题,我们从继承转组合以后的虚函数哪来的?我们还是要遵循留的规范,即基类为我们设置的接口虚函数。我们还是需要进行继承,完善接口规范,不过只需要继承流的基类即可
    //扩展操作
    class CryptoStream :public Stream{
        Stream* stream;
    public:
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
            
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream->Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream->Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    
    class BufferedStream : :public Stream{
        //...
    };
    下面是全部修改代码
    //业务操作
    class Stream{
    
    publicvirtual char Read(int number)=0;
        virtual void Seek(int position)=0;
        virtual void Write(char data)=0;
        
        virtual ~Stream(){}
    };
    
    //主体类
    class FileStream: public Stream{
    public:
        virtual char Read(int number){
            //读文件流
        }
        virtual void Seek(int position){
            //定位文件流
        }
        virtual void Write(char data){
            //写文件流
        }
    
    };
    
    class NetworkStream :public Stream{
    public:
        virtual char Read(int number){
            //读网络流
        }
        virtual void Seek(int position){
            //定位网络流
        }
        virtual void Write(char data){
            //写网络流
        }
        
    };
    
    class MemoryStream :public Stream{
    public:
        virtual char Read(int number){
            //读内存流
        }
        virtual void Seek(int position){
            //定位内存流
        }
        virtual void Write(char data){
            //写内存流
        }
        
    };
    
    //扩展操作
    class CryptoStream: public Stream {
        
        Stream* stream;//...
    
    public:
        CryptoStream(Stream* stm):stream(stm){
        
        }
        
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流  //动态特质:由组合实现
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream::Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream::Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    class BufferedStream : public Stream{
        
        Stream* stream;//...
        
    public:
        BufferedStream(Stream* stm):stream(stm){
            
        }
        //...
    };
    
    void Process(){
    
        //运行时装配
        FileStream* s1=new FileStream();
        CryptoStream* s2=new CryptoStream(s1);
        
        BufferedStream* s3=new BufferedStream(s1);
        //上面对s2加密了,我们再进行s2的缓存,实现了既加密又缓存
        BufferedStream* s4=new BufferedStream(s2);
    }

    运行时装配:

    我们编译时不存在文件加密,网络加密,文件缓存,文件加密缓存等操作,我们在运行时对其进行组合装配起来满足我们的需求

    另外注意:

    根据重构中所说:当我们类中含有重复字段和方法,我们应该将其提到前面基类中去,这里我们若是将Stream* stream提到Stream基类中去,会发现在主体类中会包含这个不需要的字段,所以这个时候我们应该设计一个中间基类。这时就引用出来了装饰模式

    (五)改进版本二(使用装饰模式<中间基类>)

    //扩展操作
    //中间类
    class DecoratorStream: public Stream{
    protected:
        Stream* stream;
    public:    
        DecoratorStream(Stream* stm):stream(stm){}
    };
    
    class CryptoStream: public DecoratorStream {
    public:
        CryptoStream(Stream* stm):DecoratorStream(stm){
        
        }
        
        
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream::Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream::Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    
    
    class BufferedStream : public DecoratorStream{    
    public:
        BufferedStream(Stream* stm):DecoratorStream(stm){
            
        }
        //...
    };

    最终规模是:1+n+1+m

    全部代码

    //业务操作
    class Stream{
    
    publicvirtual char Read(int number)=0;
        virtual void Seek(int position)=0;
        virtual void Write(char data)=0;
        
        virtual ~Stream(){}
    };
    
    //主体类
    class FileStream: public Stream{
    public:
        virtual char Read(int number){
            //读文件流
        }
        virtual void Seek(int position){
            //定位文件流
        }
        virtual void Write(char data){
            //写文件流
        }
    
    };
    
    class NetworkStream :public Stream{
    public:
        virtual char Read(int number){
            //读网络流
        }
        virtual void Seek(int position){
            //定位网络流
        }
        virtual void Write(char data){
            //写网络流
        }
        
    };
    
    class MemoryStream :public Stream{
    public:
        virtual char Read(int number){
            //读内存流
        }
        virtual void Seek(int position){
            //定位内存流
        }
        virtual void Write(char data){
            //写内存流
        }
        
    };
    
    //扩展操作
    
    DecoratorStream: public Stream{
    protected:
        Stream* stream;//...核心
        
        DecoratorStream(Stream * stm):stream(stm){
        
        }
        
    };
    
    class CryptoStream: public DecoratorStream {
    
    public:
        CryptoStream(Stream* stm):DecoratorStream(stm){
        
        }
        
        
        virtual char Read(int number){
           
            //额外的加密操作...
            stream->Read(number);//读文件流
        }
        virtual void Seek(int position){
            //额外的加密操作...
            stream::Seek(position);//定位文件流
            //额外的加密操作...
        }
        virtual void Write(byte data){
            //额外的加密操作...
            stream::Write(data);//写文件流
            //额外的加密操作...
        }
    };
    
    
    class BufferedStream : public DecoratorStream{
        
    public:
        BufferedStream(Stream* stm):DecoratorStream(stm){
            
        }
        //...
    };
    
    
    void Process(){
    
        //运行时装配
        FileStream* s1=new FileStream();
        
        CryptoStream* s2=new CryptoStream(s1);
        
        BufferedStream* s3=new BufferedStream(s1);
        
        BufferedStream* s4=new BufferedStream(s2);
    }
    以组合的方式来支持未来多态的变化

    (六)模式定义

    动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。
                                                      ——《设计模式》GoF

    (七)类图(结构)

    (八)要点总结

    1.通过采用组合而非继承的手法, Decorator模式实现了在运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

    2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又 表现为has-a Component的组合关系,即Decorator类又使用了 另外一个Component类。

    DecoratorStream: public Stream{
    protected:
        Stream* stream;//...
        
        DecoratorStream(Stream * stm):stream(stm){
        
        }
        
    };
    若是程序中一个类的父类和他的字段类是同一个类的,那么他有极大的几率是Decorator模式。因为一般继承就不组合,组合就不继承
    而我们这里的继承是为了接口的规范,组合是为了将来支持具体实现类

    3.Decorator模式的目的并非解决“多子类衍生的多继承”问题, Decorator模式应用的要点在于解决“主体类在多个方向上的扩展 功能”——是为“装饰”的含义。

    主体操作和扩展操作应该分开分之继承 

    (九)案例实现(装饰车)

    //业务规范
    class Car
    {
    public:
        virtual void show() = 0;
        virtual ~Car(){}
    };
    //主体类
    class RunCar :public Car
    {
    public:
        virtual void show()
        {
            cout << "running" << endl;
        }
    };
    
    
    class SwimCar :public Car
    {
    public:
        virtual void show()
        {
            cout << "swimming" << endl;
        }
    };
    
    class FlyCar :public Car
    {
    public: 
        virtual void show()
        {
            cout << "flying" << endl;
        }
    };
    //装饰中间类
    class DecoratorCar : public Car
    {
    protected:
        Car * car;
    public:
        DecoratorCar(Car* c) :car(c){}
    };
    //扩展操作类
    class EquipEngine :public DecoratorCar
    {
    public:
        EquipEngine(Car* c) :DecoratorCar(c)
        {
        }
    
        virtual void show()
        {
            cout << "equip engine can run fast" << endl;
            car->show();
        }
    };
    
    class EquipWing :public DecoratorCar
    {
    public:
        EquipWing(Car* c) :DecoratorCar(c)
        {
        }
    
        virtual void show()
        {
            cout << "equip wing can fly" << endl;
            car->show();
        }
    };
    
    class EquipPaddle :public DecoratorCar
    {
    public:
        EquipPaddle(Car* c) :DecoratorCar(c)
        {
        }
    
        virtual void show()
        {
            cout << "equip paddle can swing" << endl;
            car->show();
        }
    };
    int main()
    {
        SwimCar* car = new SwimCar();
        EquipPaddle *scar = new EquipPaddle(car);
        EquipEngine *ecar = new EquipEngine(scar);
        EquipWing *wcar = new EquipWing(ecar);
    
        wcar->show();
        system("pause");
        return 0;
    }

  • 相关阅读:
    P2280 [HNOI2003]激光炸弹[前缀和]
    P1280 尼克的任务[区间覆盖dp]
    P1352 没有上司的舞会[树形dp]
    HDU1024 Max Sum Plus Plus[DP]
    P1282 多米诺骨牌[可行性01背包]
    P1063 能量项链[区间DP]
    P1880 [NOI1995]石子合并[环形DP]
    P1091 合唱队形[单调性+DP]
    Gym 100971D 单调栈
    Gym 100971A Treasure Island BFS 思维题
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9532496.html
Copyright © 2011-2022 走看看