1. 桥接模式(Bridge Pattern)的定义
(1)将抽象部分与它的实现部分分离,使它们都可以独立地变化
①一般的“抽象”与“实现”是指父子类的继承关系。但这里,GoF所谓的“抽象(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; }