4."单一职责"模式

单一职责模式:在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。

典型模式:装饰模式(Decorator)、桥接模式(Bridge)。

一、装饰模式(Decorator)

1.动机

在某些情况下我们可能会"过度的使用继承来扩展对象的功能",由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并伴随着子类的增多(扩展功能的增多),各种子类(扩展功能的组合)回导致更多的子类的膨胀。

2.作用

使“对象功能的扩展”能够根据需要来动态实现;同时避免“扩展功能的增多”带来的子类膨问题,使得任何“功能扩展变化”所导致的影响力降为最低。

3.定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除了重复代码、减少子类个数)。

4.代码
//原有代码
//业务操作
class Stream{
public:
   virtual char Read(int number)=0;
   virtual void Seek(int position)=0;
   virtual void Write(char data)=0;
   virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
   virtual char Read(int number){
       //读文件流
   }
   virtual void Seek(int position){
       //定位文件流
   }
   virtual void Write(char data){
       //写文件流
   }
};
class NetworkStream :public Stream{
public:
   //...
};
class MemoryStream :public Stream{
public:
   //... 
};
//扩展操作
class CryptoFileStream :public FileStream{
public:
   virtual char Read(int number){ 
       //额外的加密操作...
       FileStream::Read(number);//读文件流     
   }
   virtual void Seek(int position){
       //额外的加密操作...
       FileStream::Seek(position);//定位文件流
   }
   virtual void Write(byte data){
       //额外的加密操作...
       FileStream::Write(data);//写文件流
   }
};
class CryptoNetworkStream : :public NetworkStream{
public:
   //...
};
class CryptoMemoryStream : public MemoryStream{
public:
   //...
};
//额外的缓冲操作...
class BufferedFileStream : public FileStream{
   //...
};
class BufferedNetworkStream : public NetworkStream{
   //...
};
class BufferedMemoryStream : public MemoryStream{
   //...
}
//额外的加密缓存操作...
class CryptoBufferedFileStream :public FileStream{
public:
   virtual char Read(int number){
       //额外的加密操作...
       //额外的缓冲操作...
       FileStream::Read(number);//读文件流
   }
   virtual void Seek(int position){
       //额外的加密操作...
       //额外的缓冲操作...
       FileStream::Seek(position);//定位文件流
   }
   virtual void Write(byte data){
       //额外的加密操作...
       //额外的缓冲操作...
       FileStream::Write(data);//写文件流
   }
};
void Process(){
   //编译时装配
   CryptoFileStream *fs1 = new CryptoFileStream();
   BufferedFileStream *fs2 = new BufferedFileStream();
   CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
}
//运用装饰模式后的代码
//业务操作
class Stream{
public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0; 
    virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }
};
class NetworkStream :public Stream{
public:
    //... 
};
class MemoryStream :public Stream{
public:
    //...
};
//扩展操作
DecoratorStream: public Stream{
protected:
    Stream* stream;//...
    DecoratorStream(Stream * stm):stream(stm){
    } 
};
class CryptoStream: public DecoratorStream {
public:
    CryptoStream(Stream* stm):DecoratorStream(stm){
    }
    virtual char Read(int number){   
        //额外的加密操作...
        stream->Read(number);//读文件流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        stream::Seek(position);//定位文件流
    }
    virtual void Write(byte data){
        //额外的加密操作...
        stream::Write(data);//写文件流
    }
};
class BufferedStream : public DecoratorStream{
    Stream* stream;//...
public:
    BufferedStream(Stream* stm):DecoratorStream(stm){ 
    }
    //...
};
void Process(){
    //运行时装配
    FileStream* s1=new FileStream();
    CryptoStream* s2=new CryptoStream(s1);
    BufferedStream* s3=new BufferedStream(s1); 
    BufferedFileStream* s4=new BufferedStream(s2);//加密且缓存
}
5.解析

这是一个内容流程序,有读、定位(搜索)和写等功能,在业务上有文件流、网络流和内存流等,扩展出的功能包括额外加密操作和缓存操作等。

在原有代码中,以继承的方法扩展这些功能,很详细也很好理解,但存在以下问题:继承引入了静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合会导致更多子类的膨胀,如下图所示。假设一级功能有n种,二级功能有m种,则该程序一共有O(1+n)+O(m!)个类。

image.png

在运用装饰模式后的代码中,通过动态(组合)地给一个对象增加一些额外的职责,其结构如图所示,则该程序一共有(1+n+1+m)个类,相比之前大大减小了类的个数,且通过动态组合,将编译时依赖转化为运行时依赖。


image.png
6.结构
image.png

1.定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent(具体类,如FileStream、NetworkStream)

2.定义一个对象,可以给这个对象添加一些职责
Decorator(装饰抽象类,如DecoratorStream)

3.维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
ConcreteDecorator(具体装饰类,如CryptoStream、BufferedStream)

4.向组件添加职责

7.总结

1.通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。

3.Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——“装饰”的含义。

二、桥接模式

1.动机

由于某些类型固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。

2.作用

使得该类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度,以应对这种“多维度的变化”。

3.定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。

4.代码
//原有代码
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:
    //**********
};
//业务抽象
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:  
    //**********
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
    //**********
};
void Process(){
        //编译时装配
        Messager *m = new MobileMessagerPerfect();
}
//运用桥接模式后代码
class Messager{
protected:
     MessagerImp* messagerImp;//...
public:
    Messager(MessagerImp* messagerImp):messagerImp(messagerImp){
    }
    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:
    //**********
};
//业务抽象 m
//类的数目:1+n+m
class MessagerLite :public Messager {
public:
    MessagerLite (MessagerImp* messagerImp):Messager(messagerImp){
    }
    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:
    MessagerPerfect (MessagerImp* messagerImp):Messager(messagerImp){
    }
    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);
}
5.解析

这是一个关于在不同平台(PC和Moblie)上,实现不同业务(MessagerLite、MessagerPerfect)的程序。

原有的代码与装饰模式中原有代码类似,通过不断地继承以扩展功能,但缺点也是显而易见的,容易造成子类的庞大不灵活。

在运用桥接模式的代码中,将抽象部分(业务功能)与实现部分(平台实现)分离,使得抽象和实现可以沿着各自的维度来变化。

6.结构
image.png

其中,

1.Abstraction:定义抽象类的接口;维护一个指向Implementtor类型对象的指针;

2.RefinedAbstraction:扩充由Abstraction定义的接口;

3.Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口完全一致,甚至可以完全不同;

4.ConcreteImplementor:实现Implementor接口并定义它的具体实现。

7.总结

1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。

2.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。 Bridge模式是比多继承方案更好的解决方法。

3.Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。

转载:https://www.cnblogs.com/gql-live-laugh-love/p/11922336.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容