单一职责”模式:
在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
典型模式
• Decorator
• Bridge
动机(Motivation)
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?
代码示例
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{ //PC平台
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerBase : public Messager{ //Mobile平台
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象
class PCMessagerLite : public PCMessagerBase { // PC平台精简版
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 { // PC平台完美版
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();
}
Messager功能模块,有PC平台和Mobile平台,而各个平台上又有精简版和完美版。PC平台和Mobile平台中,PlaySound()、DrawShape()、WriteText()、Connect()等函数的实现不同。
如果有m个平台,每个平台有n个版本,则类的数目就有 1 + n + m*n,数量会非常大。而类里面又有重复代码。
将继承关系转为组合
class PCMessagerLite { // PC平台精简版
PCMessagerBase* message;
public:
virtual void Login(string username, string password){
message->Connect();
//........
}
// 其他函数,省略
}
class MobileMessagerLite{
MobileMessagerBase* message;
// 其他函数,省略
}
进一步转变
class PCMessagerLite {
Messager* message; // new PCMessagerBase()
//....
}
class MobileMessagerLite{
Messager* message; // new MobileMessagerBase()
//....
}
这两个类就可以合为一个
class MessagerLite{
Messager* message;
//....
}
但是,有一个问题,PCMessagerBase是一个抽象类,因为它只override了基类Messager的部分纯虚函数。Messager中,应该把Login、SendMessage、SendPicture和PlaySound、DrawShape、WriteText、Connect拆分开。这是不同的变化方向,一个是平台相关的,一个是业务相关的。
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 {
// MessagerImp* messagerImp; 本来应该有这一句,但是提到了父类中
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 {
// MessagerImp* messagerImp; 往上提
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);
}
模式定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。 ——《设计模式》GoF
Abstraction——抽象化角色
他的主要职责是定义出该角色的行为,同时保存在一个对实现化角色的引用,该角色一般是抽象类。
Implementor——实现化角色
他是接口或者抽象类,定义角色必须的行为和属性。
RefinedAbstraction——修正抽象化角色
他引用实现化角色对抽象化角色进行修正。
ConcreteImplementor——具体实现化角色
他实现接口或抽象类定义的方法和属性。
要点总结
Bridge模式使用**“对象间的组合关系**”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式