“单一职责”模式
在软件组件的设计中,如果职责划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类极具膨胀,同时充斥着重复代码,这时候的关键是划清责任。
典型模式
- Decorator
- Bridge
Bridge(桥模式)
- 动机
- 由于某些类型的固有的实现逻辑,使得他们具有两个变化的维度,乃至多个纬度的变化
- 如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松的沿着两个乃至多个方向变化,而不引入额外的复杂度?
代码举例:
class Messager
{
public:
Messager()
{
}
~Messager()
{
}
public:
virtual void Login(string username, string password) = 0;
virtual void SeendMessage(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;
}
// 平台实现(各个平台的下面几个方法实现是不同的)
class PCMessagerBase : public Messager
{
public:
virtual void PlaySound(){
//.....
}
virtual void DrawShape(){
//.....
}
virtual void WriteText(){
//.....
}
virtual void Connect(){
//.....
}
}
class MobielMessagerBase : public Messager
{
public:
virtual void PlaySound(){
//.....
}
virtual void DrawShape(){
//.....
}
virtual void WriteText(){
//.....
}
virtual void Connect(){
//.....
}
}
上面的PC和Mobiel为什么是Base,其实就只是因为各个平台下面的方法有不同,所以才会进行分别实现。
后面发现,我们需要各个平台需要支持不同的功能,如“精简版”,“完整版”,所以就会有下面的实现:
// 精简版
class PCMessagerLite : public PCMessagerBase
{
public:
virtual void Login(string username, string password){
PCMessagerBase::Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(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 SeendMessage(string message){
PCMessagerBase::PlaySound();
//.......
//.......
PCMessagerBase::WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
PCMessagerBase::PlaySound();
//.......
//.......
PCMessagerBase::DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MobielMessagerLite : public MobielMessagerBase
{
public:
virtual void Login(string username, string password){
MobielMessagerBase::Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
MobielMessagerBase::WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
MobielMessagerBase::DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MobielMessagerPerfect : public MobielMessagerBase
{
public:
virtual void Login(string username, string password){
MobielMessagerBase::PlaySound();
//.......
//.......
MobielMessagerBase::Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
MobielMessagerBase::PlaySound();
//.......
//.......
MobielMessagerBase::WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
MobielMessagerBase::PlaySound();
//.......
//.......
MobielMessagerBase::DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
使用时是使用编译时装配的方式进行:
void Process(){
// 编译时装配
Messager *m = new MobielMessagerPerfect();
}
类图如下:
类的数目:1 + n + m * n
n: 第二层的变化数目
m:第三层每个m节点的变化数目
分析前面的代码可以发现,在:
virtual void Login(string username, string password){
MobielMessagerBase::PlaySound(); // 平台差异性代码
//.......
// 平台无关性代码(重复代码)
//.......
MobielMessagerBase::Connect(); // 网络连接 // 平台差异性代码
//.....登录的其他操作
// 平台无关性代码(重复代码)
//.....
}
virtual void Login(string username, string password){
MobielMessagerBase::PlaySound(); // 平台差异性代码
//.......
// 平台无关性代码(重复代码)
//.......
MobielMessagerBase::Connect(); // 网络连接 // 平台差异性代码
//.....登录的其他操作
// 平台无关性代码(重复代码)
//.....
}
看起来是有重复的,不过这个重复也是结构性的重复,如上代码中所注释,但是平台差异性代码中间又有相同的地方,它们都是同样的虚函数。
重构版本V1:
// 精简版
class PCMessagerLite // 不再继承
{
PCMessagerBase *messager;
public:
virtual void Login(string username, string password){
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MobielMessagerLite
{
MobielMessagerBase *messager;
public:
virtual void Login(string username, string password){
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
重构版本V2:
很快就会发现,上面messager指针可以进一步重构:
// 精简版
class PCMessagerLite // 不再继承
{
Messager*messager; // new PCMessagerBase()
public:
virtual void Login(string username, string password){
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MobielMessagerLite
{
Messager *messager; // new MobielMessagerBase()
public:
virtual void Login(string username, string password){
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
然后就会发现上面代码是完全重复的:
重构版本V3:
class MessagerLite
{
Messager *messager;
public:
virtual void Login(string username, string password){
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
同理完整版:
class MessagerPerfect
{
Messager *messager;
public:
virtual void Login(string username, string password){
messager->PlaySound();
//.......
//.......
messager->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager->PlaySound();
//.......
//.......
messager->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager->PlaySound();
//.......
//.......
messager->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
但是有的同学可能发现了,messager在将来new的时候是不成立的,比如::
Messager *messager;
messager = new PCMessagerLite();
是错误的,因为PCMessagerLite是抽象类,只实现了基类的部分方法。导致这个问题的原因是,Messager类中前后几个算法,放在一个类中是不合适的,因为PCMessagerBase类实现了一部分接口,PCMessagerLite又实现了另外一部分接口。
所以需要将Messager接口拆分:
class Messager
{
public:
Messager()
{
}
~Messager()
{
}
public:
virtual void Login(string username, string password) = 0;
virtual void SeendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
}
// 平台实现
class MessagerImp
{
public:
Messager()
{
}
~Messager()
{
}
public:
// 平台有差异的方法
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
}
// 平台实现(各个平台的下面几个方法实现是不同的)
class PCMessagerBase : public MessagerImp
{
public:
virtual void PlaySound(){
//.....
}
virtual void DrawShape(){
//.....
}
virtual void WriteText(){
//.....
}
virtual void Connect(){
//.....
}
}
class MobielMessagerBase : public MessagerImps
{
public:
virtual void PlaySound(){
//.....
}
virtual void DrawShape(){
//.....
}
virtual void WriteText(){
//.....
}
virtual void Connect(){
//.....
}
}
// 继承平台无关的接口,组合平台相关的类
class MessagerLite : public Messager
{
MessagerImp *messager_imp;
public:
virtual void Login(string username, string password){
messager_imp->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager_imp->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager_imp->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MessagerPerfect : public Messager
{
MessagerImp *messager_imp;
public:
virtual void Login(string username, string password){
messager_imp->PlaySound();
//.......
//.......
messager_imp->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager_imp->PlaySound();
//.......
//.......
messager_imp->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager_imp->PlaySound();
//.......
//.......
messager_imp->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
马丁福勒设计的原则,如果子类有相同的字段,可将该字段移到父类中,上面MessagerImp *messager_imp;是重复的,所以可以仍绕Messager中:
重构版本V4:
class Messager
{
protected:
MessagerImp *messager_imp;
public:
Messager()
{
}
~Messager()
{
}
public:
virtual void Login(string username, string password) = 0;
virtual void SeendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
}
class MessagerLite : public Messager
{
public:
virtual void Login(string username, string password){
messager_imp->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager_imp->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager_imp->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
class MessagerPerfect : public Messager
{
public:
virtual void Login(string username, string password){
messager_imp->PlaySound();
//.......
//.......
messager_imp->Connect(); // 网络连接
//.....登录的其他操作
//.....
}
virtual void SeendMessage(string message){
messager_imp->PlaySound();
//.......
//.......
messager_imp->WriteText(); // 写信息
//..... // 发送信息的其他操作
//.....
}
virtual void SendPicture(Image image){
messager_imp->PlaySound();
//.......
//.......
messager_imp->DrawShape(); // 绘制图片
//..... // 发送图像的其他操作
//.....
}
}
使用的位置修改为:
void Process()
{
// 运行时装配
MessagerImp *m_imp = new PCMessagerBaes(); // 这个类名应该修改为PCMessagerImp
Messager *m = new Messager(m_imp);
}
类图:
类的数目变成了1+n+m
第一个版本的Messager中两类不同的虚函数,就表示了不同的变化方向:
- 业务抽象
- 平台实现
模式定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立变化
—《设计模式》GOF
结构
稳定和变化
要点总结
- Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度变化,所谓抽象和实现沿着各自维度的变化,即“子类化”它们
- Bridge模式有时候类似于多继承方案,但是多继承往往违背了单一职责原则,复用性比较差,Bridge模式是比多继承更好的解决方法。
- Bridge模式的应用,一般在“两个非常强的变化维度”,有时一个类也有多于2个的变化维度,这时可以使用Bridge的扩展模式