第22章 手机软件何时统一--桥接模式
22.1 凭什么你的游戏我不能玩
其实手机真正的发展也就近十年,此期间各大手机厂商都发展自己的软件部门开发手机软件,哪怕是同一品牌的手机,不同型号的也完全有可能软件不兼容。型号不同,软件还算是基本兼容,可惜不同品牌,软件基本还是不能整合在一起。
22.2 紧耦合的程序演化
手机游戏类,M品牌手机游戏和N品牌手机游戏
由于手机都需要通讯录功能,于是N品牌和M品牌都增加了通讯录的增删改查功能。
在很多情况下用继承会带来麻烦。比如对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
在面向对象中有一个很重要的设计原则,那就是合成/聚合复用原则。即优先使用对象合成/聚合,而不是类的继承。
22.3 合成/聚合复用原则
聚合是一种弱的拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分,合成则是一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
合成/聚合复用原则的好处是。优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
按照上面的例子说就是需要学会用对象的职责,而不是结构来考虑问题,其实答案就在上面说到的手机和PC电脑的差别上。手机是不同的品牌公司,各自做自己的软件,这样就和22.2种的代码设计一样了。
22.4 松耦合的程序
手机品牌类
品牌N品牌M具体类
#pragma once #include "handsetsoft.h" class HandsetBrand { public: void SetHandsetSoft(HandSetSoft* p) { m_Soft = p; }; virtual void Run(void) = 0; virtual ~HandsetBrand() { delete m_Soft; }; protected: HandSetSoft* m_Soft; }; //N Brand class HandsetBrandN :public HandsetBrand { public: void Run(void) { m_Soft->Run(); }; }; //M Brand class HandsetBrandM :public HandsetBrand { public: void Run(void) { m_Soft->Run(); }; }; //new add //S Brand class HandsetBrandS :public HandsetBrand { public: void Run(void) { m_Soft->Run(); }; };
手机软件 ,游戏,通讯录等具体类
#pragma once #include <iostream> //Mobile Soft class HandSetSoft { public: virtual void Run(void) = 0; virtual ~HandSetSoft(){}; }; class HandSetGame : public HandSetSoft { public: void Run(void) { std::cout << "Mobile Game is Starting..." << std::endl; }; }; class HandSetAddressList : public HandSetSoft { public: void Run(void) { std::cout << "Mobile AddressList is Starting..." << std::endl; }; }; // new add //MP3 class HandSetMP3 : public HandSetSoft { public: void Run(void) { std::cout << "Mobile MP3 is Starting..." << std::endl; }; };
客户端
#include "stdafx.h" #include "HandsetBrand.h" int _tmain(int argc, _TCHAR* argv[]) { HandsetBrand* ab = new HandsetBrandN(); ab->SetHandsetSoft(new HandSetGame()); ab->Run(); ab->SetHandsetSoft(new HandSetAddressList()); ab->Run(); HandsetBrand* pm = new HandsetBrandM(); pm->SetHandsetSoft(new HandSetGame()); pm->Run(); pm->SetHandsetSoft(new HandSetAddressList()); pm->Run(); delete pm; delete ab; return 0; }
现在如果要增加一个功能,比如MP3音乐播放功能,那么只要增加这个类就行了。不会影响其他任何类。 继承是一种强耦合的结构。父类变,子类就必须要变。所以我们在用继承时一定要在是“is-a”的关系时再考虑使用,而不是任何时候都去使用。
22.5 桥接模式
将抽象部分与它的实现部分分离,使他们都可以独立地变化。如图22.4所示两个抽象类之间连接一根聚合线,所以我们形象地叫他是桥接模式。桥接模式的核心意图就是把这些实现独立出来,让它们各自地变化。这就使得每一种变化不会影响到其他实现,从而达到应对变化的目的。
22.6 桥接模式基本代码
Implementor类,ConcreteImplementorA和ConcreteImplementorB等派生类
#pragma once #include <iostream> class Implementor { public: virtual void Operation(void) = 0; }; class ConcreteImplementorA : public Implementor { public: void Operation(void) { std::cout << "ConcreteImplementorA..." << std::endl; }; }; class ConcreteImplementorB : public Implementor { public: void Operation(void) { std::cout << "ConcreteImplementorB..." << std::endl; }; };
Abstraction类,RefinedAbstrcation类
#pragma once #include "Implementor.h" class Abstraction { public: void SetImplementor(Implementor* p) { m_Impl = p; }; virtual void DoOperation(void) { m_Impl->Operation(); } virtual ~Abstraction() { delete m_Impl; }; protected: Implementor* m_Impl; }; class RefinedAbstraction : public Abstraction { public: void DoOperation(void) { m_Impl->Operation(); } };
#include "abstraction.h" int _tmain(int argc, _TCHAR* argv[]) { RefinedAbstraction* ab = new RefinedAbstraction(); ab->SetImplementor(new ConcreteImplementorA()); ab->DoOperation(); ab->SetImplementor(new ConcreteImplementorB()); ab->DoOperation(); delete ab; return 0; }
桥接模式强调的是将抽象部分和实现部分分离。说得通俗点就是实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让他们独立变化,减少他们之间的耦合。