由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度。
其实看来上边那两句话的时候我愣住了。
啊?这也是多个方向?
啊?这个也是解决子类过多问题?
啊?这跟装饰器有啥区别?
后来我才知道,李建忠老师的意思跟我想的不一样,桥模式他解决的是类太冗余了的问题。装饰器那个例子大家可能还记得,不管是Buffer和Crypto还是Network、File的,他们重写的都是read、write和seek函数。而桥模型的核心在于你这个类派生的方向完全不搭嘎。什么意思?你这基类中的成员函数压根就不能放在一个类里边!说好的单一职责呢?下边举个例子
比如我要开发个即时通信软件吧,想想有什么功能呢?
登录Login
发消息SendMessage
发图片SendMessage当然还有其他功能,先说这么多吧。(发表情包发文件什么的)
还有:
发出声音PlaySound
显示图像ShowPictrue
写文案(要发消息的嘛)WriteText
连接操作connect
可以看到这里笔者将这七个函数分成了两个阵营。前三个是业务,与用户面对面,我要完成什么,是一连串动作的合集。后四个是具体功能,这是最基本的支撑。说白了前三个是后四个的组合。
这个类有两个派生方向,第一个是平台支持。也就是后四个,这是基本功能,你要适配相应的平台,例如PC端,移动端等,那派生两个类咯。PCMessager和MobileMessage。第二个派生方向是业务,简易版和豪华版,就像TIM和QQ的区别嘛,阉割一点功能。好!那就是针对PCMessager和MobileMessage,我还得派生出PCMessagerLite、PCMessagerPerfect、MobileMessagerLite、MobileMessagerPerfect。看到这我直呼内涵!好家伙!我知道,装饰器!一毛一样。但是大家有没有发现,两个派生的方向,重写的函数根本没关系。
贴个原来的代码
class Messager{
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture(Image image)=0;
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual ~Messager(){}
};
//平台实现
class PCMessagerBase : public Messager{
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerBase : public Messager{
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象
class PCMessagerLite : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::DrawShape();
//........
}
};
class PCMessagerPerfect : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::DrawShape();
//........
}
};
class MobileMessagerLite : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::DrawShape();
//........
}
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::DrawShape();
//........
}
};
void Process(){
//编译时装配
Messager *m =
new MobileMessagerPerfect();
}
那怎么办,拆!
不该放一块的别硬放
class Messager{
protected:
MessagerImp* messagerImp;//...
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture(Image image)=0;
virtual ~Messager(){}
};
class MessagerImp{
public:
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual MessagerImp(){}
};
//平台实现 n
class PCMessagerImp : public MessagerImp{
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerImp : public MessagerImp{
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象 m
//类的数目:1+n+m
class MessagerLite :public Messager {
public:
virtual void Login(string username, string password){
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image){
messagerImp->DrawShape();
//........
}
};
class MessagerPerfect :public Messager {
public:
virtual void Login(string username, string password){
messagerImp->PlaySound();
//********
messagerImp->Connect();
//........
}
virtual void SendMessage(string message){
messagerImp->PlaySound();
//********
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image){
messagerImp->PlaySound();
//********
messagerImp->DrawShape();
//........
}
};
void Process(){
//运行时装配
MessagerImp* mImp=new PCMessagerImp();
Messager *m =new Messager(mImp);
}
MessagerImp是用来做平台层面的适配的,Messager是用来做业务的,这就是两个方向了。但是呢,你做业务得要平台支撑吧?所以Message里边有一个messagerImp。直接提到基类来,因为业务方向上的所有派生类都必不可少这玩意儿,共有字段那就提上来呗。
桥模型的桥的体现,就在这了(Message里边有一个messagerImp)