设计模式-单一职责模式

简介: 设计模式-单一职责模式

Decorator

  • 动机
  • 在某些情况下我们可能会 “过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使得 “对象功能的扩展” 能够根据需求来 动态 的实现?同时避免“扩展功能的增多带来的子类膨胀问题?使功能的拓展变化导致的影响最低?
  • 定义

动态(组合)地给一个对象增加一些额外的职责。就增加功

能而言,Decorator 模式比生成子类(继承)更为灵活(消

除重复代码 & 减少子类个数)。

——《设计模式》GoF

  • 举例
    比如设计一个流系统,开始分别有FileStream、NetworkStream、 MemoryStream,因为这些类使用相同的函数,但是面向对象不同,因此可以有一个虚基类Stream作为他们的父类。
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{ ...};
    class NetworkStream: public Stream{ ...};
    class MemoryStream: public Stream{ ...};
  • 但是当后期想扩展功能时,比如在FileStream的基础上扩展出CryptoFileStream BufferedFileStream等,如果NetworkStream、MemeoryStream类也有这样的需求,如果直接使用子类继承的方式设计,就会形成如下的类图:

    设,二级子类(图中第二层的类)有n个,三级子类有m个,那么共需要 1+m+mn 个类别,并且包含了大量的重复代码。
    以Cryptoxxxtream为例:
class CryptoFileStream :public FileStream{
    public:
        virtual char Read(int number) override;
        virtual void Seek(int position) override;
        virtual void Write(char data) override;
    };
    class CryptoNetworkStream : :public NetworkStream{
    public:
        virtual char Read(int number) override;
        virtual void Seek(int position) override;
        virtual void Write(char data) override;
    };
  • CryptoFileStream和CryptoNetworkStream接口一致,只是面向不同的平台应用不同,导致了代码冗余。当扩展功能增多,那么冗余的更多,可维护性就更差。
  • Decorator应用此模式时的类图:                           图2在上图2中,新拓展的功能类没有直接继承相对应的父类,而是通过加入一个Decotator类,使得在扩展功能类中到底继承哪个父类延迟到子类中。
class Decorator: public Stream{
    protected:
        Stream* _stream;
        Decorator(Stream * stm):_stream(stm)
        { }
    };
  • Decoraotr最大特点是:继承 stream 的同时也包含了一个 stream 类型的字段_stream。
  • 继承stream:是为了继承stream的接口函数
  • _stream字段:是为了使用 多态,在后续的功能扩展中,在运行时再决定扩展FileStream、NetworkStream、MemoryStream中的哪个对象。
    比如 :
//运行时装配
    FileStream* s1=new FileStream();
    // 扩展FileStream --> CryFileStream
    CryptoStream* s2=new CryptoStream(s1);
    // 扩展FileStream --> BufferedStreamFileStream
    BufferedStream* s3=new BufferedStream(s1);
    // 扩展CryFileStream --> CryBufferedStreamFileStream
    BufferedStream* s4=new BufferedStream(s2);
  • 此时相比原本的设计,需要的类数是:1+n+1+m,由此可见代码复杂度降低了,可维护性上升了。
    这个案例中,变化的是那些扩展的功能,不变的FileStream、NetworkStream、MemoryStream以及Stream。
  • 总结
  • 通过采用 组合而非继承 的手法, Decorator 模式实现了在运行时 动态扩展对象功能 的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”
  • Decoraotr 类在 接口上表现为 is-a Component 的继承关系 ,即Decorator类继承了Component类所具有的接口。但 在实现上又表现为 has-a Component 的组合关系,即 Decoraotr r类又使用了另外一个Component类。
  • Decoraotr 模式的目的并非解决“多子类衍生的多继承”问题,Decoraotr 模式应用的要点在于解决 “主体类在多个方向上的扩展功能” 。

Bridge

  • 动机
  • 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化
  • 如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度
  • 模式定义

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

——《设计模式》GoF

  • 举例比如要设计一个Messageer类来应对不同的平台需求,比如有PC和Mobile两个平台:
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(){}
    };
  • 将Meassger抽象成虚基类,让子类来继承实现。 比如:
// pc平台
    class PCMessagerBase : public Messager{
    public:
        virtual void PlaySound(){
            //**********
        }
        virtual void DrawShape(){
            //**********
        }
        virtual void WriteText(){
            //**********
        }
        virtual void Connect(){
            //**********
        }
    };
    // mobile平台
    class MobileMessagerBase : public Messager{
    public:
        virtual void PlaySound(){
            //==========
        }
        virtual void DrawShape(){
            //==========
        }
        virtual void WriteText(){
            //==========
        }
        virtual void Connect(){
            //==========
        }
    };
  • 如此设计容易出现两个问题:
  • 假设针对 PCMessagerBase 扩展出两个需求,一个是精简版,一个完全版本。在登录login、sendMessgae以及sendPicture时效果不同,以其中一个需求为例:
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();
        //........
    }
};
  • 可以看出,PCMessagerPerfect只重新实现login、sendMessgae、sendPicture,而其余的函数使用父类。而父类PCMessagerBase只实现后四个函数,前三个却不想实现,因为假设在不同平台只是后面的四个函数操作不同,前面三个一致。
    而产生这个问题,在于类Messager里面有朝着两个方向的发展,那么就需要将其分开来,变成两个类,让子类选择自己变化方向的类来继承:
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(){}
};
  • 将后四个函数的具体业务实现剥离开来,然后组合该类的一个指针对象。那么子类也可以很方便的选择自己需求方向来继承:
// 平台实现
class PCMessagerImp : public MessagerImp{
public:
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};
// 业务抽象
class MessagerLite :public Messager {
public:
    virtual void Login(string username, string password){
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        messagerImp->DrawShape();
        //........
    }
};
  • 在 MessagerLite 实现业务,PCMessagerImp实现逻辑, 这是两个方向的变化 。通过前者的messagerImp指针实现运行时函数调用确定。通过这种组合可以实现相互组合:MessagerImp可以开发出针对更多的平台(n个),Messager开发出更多的设计需求(m个),两个可以任意结合。然而只是需要 2+n+m 个类,就能设计出 n*m 个结果。
  • 总结
  • Bridge模式使用 “对象间的组合关系” 解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以 沿着各自的维度来变化 。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法
  • Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式
相关文章
|
15天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
18天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
11天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
29 1
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
21 3
|
2月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
2月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
35 0
|
2月前
|
设计模式 Java
Java设计模式-工厂方法模式(4)
Java设计模式-工厂方法模式(4)

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78