zoukankan      html  css  js  c++  java
  • 第9章 结构型模式—桥接模式

    1. 桥接模式(Bridge Pattern)的定义

    (1)将抽象部分与它的实现部分分离,使它们都可以独立地变化

      ①一般的“抽象”与“实现”是指父子类的继承关系。但这里,GoF所谓的“抽象”是如果引起一个类变化是多维度的因素(设为2维),就将其他变化因素抽象成一个接口,在“Abstraction类”中只留这个接口,然后通过对象组合(而不是继承)的方式去依赖这个接口。而“实现”是指在让另一个“Implementor类”的子类去实现接口(第2维度的变化)。

      ② “Abstraction类”和“Implementor类”分别代表了引起类变化的两个维度(前者是个抽象类、后者是接口),正因为这种分离,所以他们都可以独立的变化。

      ③简单的理解就是,在类中抽离“方法”形成另一个“类”。如动物狗,在设计时,把狗设计成一个类,里面的“行走”方法从狗中分离出来,形成“行走”接口,并在“狗”类中使用这个“行走”对象。这样“狗”和“行走”类都可以独立变化。

    (2)桥接模式的结构和说明

     

      ①Abstraction:抽象部分的接口。通常在这个对象中,要维护一个实现部分的对象的引用,抽象对象里面的方法,需要调用实现部分的对象来完成。这个对象中的方法,通常都是和具体的业务相关的方法。

      ②RefinedAbstraction:扩展抽象部分的接口。通常在这些对象中,定义跟实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法,也可能需要调用实现部分的对象来完成。如上图中的operation方法中,一般调用impl->operationImpl();

      ③Implementor:定义实现部分的接口。这个接口不用和Abstraction中的方法一致。

      ④ConcreteImplementor:真正实现Implementor接口的对象。

    (3)不使用桥接模式的案例

      ①蜡笔和毛笔:这两者的关键区别在于笔和颜色是否能够分离

     

      ②跨平台的图像浏览系统

     

      A、使用多层继承结构,导致系统中类的个数急剧增加(共17个)。

      B、系统扩展麻烦,由于每个具体类(*Image)既包含图像文件格式信息,又包含操作系统信息。因此无论是增加新的图像文件格式还是增加新的操作系统,都需要增加大量的具体类。(如增加TIF,则需要增加3个具体类以便在3种不同的操作系统中显示。如果增加一个新的操作系统,则需要在每个*Image下增加具体的类。

      C、从图中可以看出,该系统存在两个独立变化的维度图像文件格式和操作系统

     

    (4)思考Bridge模式

      ①Bridge模式的本质分离抽象和实现。它是解决多继承的一套方案,把使用继承改成使用对象组合,解耦了抽象和实现之间固有的绑定关系,从而使“抽象”和“实现”可以沿着各自的纬度独立的变化。

      ②Bridge模式的动机:Bridge模式就是为了处理类的多维度变化,其目的是解耦

      ③由于抽象部分和实现部分是完全分离的。所以可以在运行时动态组合具体的真实实现,从而动态变换功能。此外,同一个真实实现可以被不同抽象对象使用;反过来,同一个抽象也不能有多个不同的实现。

      ④桥接模式是一种很实用的结构型设计模式,如果某个类存在多个独立变化的维度,通过该模式可以将这多个维度分离出来,使它们可以独立扩展。让系统更符合“单一职责原则”。

    【编程实验】发送提示消息

      

    //结构型模式:桥接模式
    //场景:发送提示消息
    //消息:普通消息、加急消息和特急消息
    //发送方式:站内消息、手机短信、E-mail
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    //实现发送消息的统一接口
    class MessageImplementor
    {
    public:
        //发送消息:
        //@param message 要发送的消息内容
        //@param toUser  消息发送的目的人员
        virtual void send(string message,string toUser) = 0; 
    };
    
    //抽象的消息对象
    class AbstractMessage
    {
    private:
        //持有一个实现部分的对象
        MessageImplementor& impl;
    public:
        //构造函数,传入实现部分的对象
        AbstractMessage(MessageImplementor& mi):impl(mi){}
        
        //发送消息,转调实现部分的方法
        virtual void sendMessage(string message,string toUser)
        {
            impl.send(message, toUser);
        }
    };
    //************************具体的消息发送方式*******************
    //站内消息的实现
    class MessageSMS : public MessageImplementor
    {
    public:
        void send(string message, string toUser)
        {
            cout <<"SMS:""<< message << "" to:" << toUser << endl;
        }    
    };
    
    //E-mail方式的发送消息
    class MessageEmail : public MessageImplementor
    {
    public:
        void send(string message, string toUser)
        {
            cout <<"Email:""<< message << "" to:" << toUser << endl;
        }    
    };
    
    //Mobile方式的发送消息
    class MessageMobile : public MessageImplementor
    {
    public:
        void send(string message, string toUser)
        {
            cout <<"Mobile:""<< message << "" to:" << toUser << endl;
        }    
    };
    
    //*********************具体的消息类型************************
    //普通消息
    class CommonMessage : public AbstractMessage
    {
    public:
        CommonMessage(MessageImplementor& im):AbstractMessage(im){} 
    };
    
    //加急消息
    class UrgencyMessage : public AbstractMessage
    {
    public:
        UrgencyMessage(MessageImplementor& im):AbstractMessage(im){} 
        void sendMessage(string message,string toUser)
        {
            message ="Urgency:" + message;
            AbstractMessage::sendMessage(message, toUser);
        }
    };
    
    //特急消息
    class SpecialUrgencyMessage : public AbstractMessage
    {
    public:
        SpecialUrgencyMessage(MessageImplementor& im):AbstractMessage(im){} 
        void sendMessage(string message,string toUser)
        {
            message ="SpecialUrgency:" + message;
            AbstractMessage::sendMessage(message, toUser);
            //还需要加一条待催促的信息
            hurry(100);
        }
        
        void hurry(int messageId)
        {
            //执行摧促的业务,发出催足的信息,这里简单示意一下
            cout << "hurry: messageId=" << messageId << endl; 
        }
    };
    
    int main()
    { 
        //1、使用站内短息发送消息(普通、加急、特急等)
        //创建具体的实现对象
        MessageImplementor* impl = new MessageSMS();//选择站内短息的方式来发送
        
        //创建一个普通消息对象
        AbstractMessage* m = new CommonMessage(*impl);    //发送普通消息
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m;
        
        //创建一个加急消息对象
        m = new UrgencyMessage(*impl);  //发送加急消息
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m;  
        
        //创建一个特急消息对象    
        m = new SpecialUrgencyMessage(*impl); //发送特急消息
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m; 
    
        delete impl;
        cout <<endl;
        
        //2、把实现切换成手机短信,然后再实现一遍    
         //创建具体的实现对象
        impl = new MessageMobile();
        
        //创建一个普通消息对象
        m = new CommonMessage(*impl);
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m;
        
        //创建一个加急消息对象
        m = new UrgencyMessage(*impl);
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m;  
        
        //创建一个特急消息对象    
        m = new SpecialUrgencyMessage(*impl);
        m->sendMessage("Cup of Tee please","SantaClaus");
        delete m; 
           
        return 0;
    }
    View Code

    2. 谁来桥接:谁也创建Implementor对象?

    (1)方式1:由客户端创建Implementor对象,并设置到抽象部分的对象中去。

    (2)方式2:可以在抽象部分对象构建的时候,由抽象部分的对象自己来创建。当然也可以给这个抽象部分的对象传递一些参数,让它可以根据参数来创建具体的Implementor对象。

    (3)方式3:可以在Abstraction中选择并创建一个默认的Implementor对象,然后子类可以根据需要改变这个实现。

    (4)方式4:可以使用抽象工厂或简单工厂来选择并创建具体的Implementor对象。抽象部分的类可以通过调用工厂的方法来获取Implementor对象。

    3. 桥接模式的优点

    (1)分离抽象和实现部分。让抽象和实现部分可以独立变化。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以,将依赖实现改为依赖接口编程。

    (2)更好的扩展性:抽象和实现部分可以别分独立的扩展,而不会互相影响

    (3)可以动态切换实现:因为一个实现不再是固定绑定到一个抽象接口上,所以可以实现在运行期间动态地切换。

    (4)可减少子类的个数:如果采用继承的实现方式,大约需要两个纬度上可变化数量的乘积的子类个数;而采用桥接模式只需要两个纬度可变化数量的子类的和个子类。

    4. 桥接模式的使用场景

    (1)需要跨越多个平台的图形和窗口中系统上。

    (2)一个类存在两个独立变化维度,且两个维度都需要扩展。

    (3)三层架构中通过桥接模式将业务逻辑层(BLL)与数据操作层(DAL)解耦

    5. 三层架构及层间的解耦

    (1)三层划分:用户界面层(UI)、业务逻辑层(BLL)和数据访问层(DAL)

     

    (2)各层的作用

      ①用户界面层:只负责显示和采集用户操作。

      ②业务逻辑层:负责UI和DAL层之间的数据交换,是系统架构中体现核心价值的部分。它关注点主要集中在业务规则的制定、业务流程的实现和业务需求的有关系统设计。它与系统所对应的领域(Domain)有关。也可以做一些如用户权限合法性和数据据式是否正确等验证检查。

      ③数据访问层:与数据库直接打交道,如数据库进行增、删、改、查等操作。常用的技术如ADO+SQL语句。这里可能还会有DBUtility类(负责建立数据库连接)、与工厂方法模式相关的DALFactory抽象类(用于创建如OracleDAL、SQLServerDAL对象之类的工厂)、DataAccess产品类(真正操作数据库的类)

    (3)各层的交互

      ①UI和BLL:UI层引用BLL和实体类(Entity)。

      ②BLL和DAL桥接模式,在BLL中持有一个DAL接口的引用

      ③DAL与数据库:工厂模式或抽象工厂模式。DAL与数据库间的解耦可参考《抽象工厂模式》部分的内容

    【编程实验】利用桥接模式实现三层架构中的业务逻辑层(BLL)与数据访问层(DAL)的解耦。

     

    //结构型模式:桥接模式
    //场景:三层架构业务逻辑层(BLL)与数据操作层(DAL)的解耦
    
    #include <iostream>
    #include <string>
    #include <list>
    using namespace std;
    
    //************************Implementor类******************
    //三层架构中的数据访问层(DAL)
    class IDataAccess
    {
    public:
        virtual void addRecord(string name) = 0;
        virtual void deleteRecord(string name) = 0;
        virtual void updateRecord(string name) = 0;
        virtual string getRecord(int index) = 0;
        virtual void queryAllRecords() = 0;   
    };
    
    //具体的DAL层
    class CustomerDataAccess: public IDataAccess
    {
    private:
        list<string> customers;
    public:
        CustomerDataAccess()
        {
            //实际工程中从数据库中读取数据再填充列表
            customers.push_back("learning Hard");
            customers.push_back("zhang san");
            customers.push_back("Li si");
            customers.push_back("Wang wu");
        }   
        
        void addRecord(string name)
        {
            customers.push_back(name);
        }
        void deleteRecord(string name)
        {
            customers.remove(name);
        }
        void updateRecord(string name)
        {
            //演示目的
             customers.front() = name;
        }
        string getRecord(int index)
        {
            list<string>::iterator iter = customers.begin();
            advance(iter,index);//取出索引号为index的元素
            
            return *iter;
        }
        void queryAllRecords()
        {
            for(list<string>::iterator iter=customers.begin();iter != customers.end();++iter)
            {
                cout << *iter << endl;
            }
        }    
    };
    
    //BLL层的接口
    class AbstractBusiness
    {
    private:
        IDataAccess* dataAccess;
    public:
        AbstractBusiness(IDataAccess* dataAccess)
        {
            this->dataAccess = dataAccess;
        }
        
        virtual void add(string name)
        {
            dataAccess->addRecord(name);   
        }
        
        virtual void del(string name)
        {
            dataAccess->deleteRecord(name);
        }
        
        virtual void update(string name)
        {
            dataAccess->updateRecord(name);
        }
        
        virtual string get(int index)
        {
            return dataAccess->getRecord(index);
        }
        
        virtual void showAll()
        {
           dataAccess->queryAllRecords();       
        }
    };
    
    //具体的业务逻辑类
    class CustomersBusiness: public AbstractBusiness
    {
    public:
        CustomersBusiness(IDataAccess* dataAccess):AbstractBusiness(dataAccess){}
        
        void showAll()
        {
            cout <<"-----------------------"<<endl;
            AbstractBusiness::showAll();
            cout <<"-----------------------"<<endl;
        }    
    };
    
    int main()
    { 
        IDataAccess* dataAccess = new CustomerDataAccess();
        AbstractBusiness* customers = new CustomersBusiness(dataAccess);
        
        customers->add("SantaClaus");
        cout<<"add a member:"<<endl;
        customers->showAll();
        
        cout <<"delete a member:" << endl;
        customers->del("Wang wu");
        customers->showAll();
        
        cout << "update a member:" << endl; 
        customers->update("Learning Hard");
        customers->showAll();
       
        return 0;
    }
  • 相关阅读:
    快速掌握GIT
    Codeigniter+PHPExcel导出Excel文件
    git结构拓扑图
    (转)MVC新手指南
    (转)jQuery插件开发 其实很简单
    (转)Asp.net缓存简介
    (转)让ASP.NET MVC页面返回不同类型的内容
    (转)2010 .NET面试题整理之基础篇
    (转) .NET对象的XML序列化和反序列化
    (转)2010面试攻略
  • 原文地址:https://www.cnblogs.com/5iedu/p/5521590.html
Copyright © 2011-2022 走看看