第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;
}
桥接模式强调的是将抽象部分和实现部分分离。说得通俗点就是实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让他们独立变化,减少他们之间的耦合。