Loading

设计模式总结:结构型模式

结构型模式

结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。

  • 类模式
    • (类)适配器模式
  • 对象模式
    • (对象)适配器模式
    • 桥接模式
    • 组合模式
    • 装饰模式
    • 外观模式
    • 享元模式
    • 代理模式
  1. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  2. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  3. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
  4. 装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
  5. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
  6. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
  7. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。

1 适配器模式 Adapter

image-20210624170829671

image-20210624170852197

1.1 模式动机

  • 客户端通过目标类的接口来访问它提供的服务,但有时候现有的接口需要转化为客户类期望的接口,以保证对现有类的重用。
  • 定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者 (Adaptee),即被适配的类
  • 适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转 化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的 方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机

1.2 定义

  • 适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。 适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

包含以下角色:

  • Target 目标抽象类
  • Adapter 适配器类
  • Adaptee 适配者类
  • Client 客户端

类适配器

image-20210624171021744

对象适配器

image-20210624171037128

1.3 模式分析

  • 优点
    • 目标类和适配者类解耦
    • 增加了类的透明性和复用性,将具体的实现封装在适配器中
    • 灵活性和可扩展性好,通过配置文件更换适配器,符合开闭原则
    • (类适配器)是适配者的子类,可以在适配器中更换一些适配者的方法,使得适配器灵活性更强
    • (对象适配器)可以把多个不同的适配者适配到同一个目标
  • 缺点
    • (类适配器)在一些语言如Java中,一次最多只能有一个适配者,有局限性
    • (对象适配器)与类适配器相比,想更换适配者的方法不容易,需要先造一个适配者的子类,再做适配器。
  • 适用
    • 需要复用现成的类,但接口不符合要求
    • 想要建立一个可复用的类,用于与一些彼此之间没用太大关联的一些类

1.4 例子

  • 仿生机器人:设计一个仿生机器人,在有cry、move等方法。要求在不修改已有代码的基础上使得机器人能像狗一样叫,像狗一样跑

    image-20210624171206215

  • 加密适配器:用户信息需要加密后存入数据库,系统已经定义好了数据库操作类,要求重用已有的加密算法,这些算法封装在第三方类库中

    image-20210624171321305

  • JDBC给出一个客户端通用的接口,每一个数据库引擎来适配JDBC驱动,即介于JDBC接口和数据库引擎接口之间的适配器软件。

1.5 模式扩展

  • 默认适配器

    不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口的每一个方法提供一个默认实现(空方法),子类可以选择性地覆盖父类的的某些方法

    image-20210624172309879

  • 双向适配器

    在适配器中有目标类和适配者类的引用时,双向适配器类既可以把适配者接口转换成目标接口,也可以把目标接口转换成适配者接口

    image-20210624172431373

2 组合模式 Composite

image-20210624172643281

2.1 模式动机

  • 客户端系统一致地处理树形结构
  • 组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象

2.2 定义

  • 组合模式(Composite Pattern):组合多个对象形成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
  • 组合模式又可以称为“整体-部分”(Part-Whole)模式,属于对象的结构模式,它将对象组织到树结构中,可以用来描述整体与部分的关系

2.3 模式分析

  • 关键:定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。
  • 容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。
  • 优点
    • 可以清楚地定义分层对象
    • 客户端可以一致地使用组合结构或是单个对象
    • 定义了一个类层次结构,可以不断递归,形成复杂的树形结构
    • 更容易在组合体内加入对象构建,客户端不必修改代码
  • 缺点
    • 使得设计更新抽象
    • 很难对容器中的构件类型进行限制
  • 适用
    • 需要表示一个对象整体或部分层次
    • 希望客户端针对抽象构建编程,无需关心具体层次
    • 对象的结构是动态的且复杂程度不一样,客户端需要一致地处理他们

2.4 例子

  • 水果盘中有水果,大水果盘中还有小水果盘。现在需要对盘中的水果遍历吃

    image-20210624172906153

  • 文件浏览

    image-20210624172956195

3 桥接模式 Bridge

image-20210624175000954

3.1 模式动机

  • 两个变化维度(即两个变化的原因)的系统,桥接模式将继承关系转换为关联关系, 从而降低了类与类之间的耦合,减少了代码编写量。

    image-20210624174910704

3.2 定义

  • 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  • 它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

包含以下角色

  • Abstraction 抽象化
  • RefinedAbstraction 扩充抽象化
  • Implementor 实现化
  • ConcreteImplementor 具体实现化

3.3 模式分析

  • 重点在于如何理解抽象化实现化脱耦
    • 抽象化:将对象的共同信息抽取出来
    • 实现化:针对抽象化给出的具体实现
    • 脱耦:将抽象化和实现化之间的耦合解脱开,或是说将他们的强关联改换成弱关联、将继承关系改为关联关系
  • 优点
    • 分离抽象接口和实现部分
    • 类似于多继承,但比多继承好
    • 提高系统的可扩展性,两个变化维度任意扩展一个,都不需要改变原系统,符合开闭原则
    • 实现细节对客户透明,可以对用户隐藏实现细节
  • 缺点
    • 增加系统理解和设计难度
    • 要求正确识别两个独立变化的维度,有局限性
  • 适用
    • 系统需要再具体化角色和抽象角色之间增加灵活性
    • 抽象化和具体化角色可独立扩展,互不影响
    • 一个类存在两个独立变化的维度
    • 不希望因为适用继承或多层次继承导致系统类个数剧增

3.4 例子

  • 模拟毛笔:大中小3种型号的画笔,能够绘制5种不同颜色。如果用蜡笔,需要15支,如果用毛笔,只需要三支笔和五种颜色的颜料

    image-20210624175535400

  • 跨平台视频播放器:Linux、Windows、Unix,播放多种格式的视频

    image-20210624175629794

  • Java虚拟机

    image-20210624180022963

3.5 模式扩展

  • 与适配器联用

    桥接模式用于系统初步设计,在初步设计完成之后,发现系统与已有类无法协调工作时,适用适配器

    image-20210624180203969

4 装饰模式 Decorator

image-20210624200843950

4.1 模式动机

  • 使用对客户透明的方式动态地给一个对象附加上更多的责任。客户端不会觉得装饰前和装饰后的对象有什么不同。
  • 装饰模式可以在不需要创造更多子类的情况下,给对象的功能加以扩展

4.2 定义

  • 装饰模式(Decorator Pattern) :在不改变现有对象结构的情况下,动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。

包含如下角色:

  • Component 抽象构件
  • ConcreteComponent 具体构件
  • Decorator 抽象装饰类
  • ConcreteDecorator 具体装饰类

4.3 模式分析

  • 与继承相比,关联关系的优势在于不会破坏类的封装性,较松耦合。缺点在于比继承关系要创建更多的对象
  • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任
  • 优点
    • 可以提供比继承更多的灵活性
    • 通过一种动态的方式来扩展一个对象的功能
    • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合
    • 具体构建类与具体装饰类可以独立变化
  • 缺点
    • 会产生很多小对象
    • 比继承更容易出错
  • 适用
    • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
    • 需要动态地给一个对象增加功能,也可以动态地被撤销
    • 当不能用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时

4.4 例子

  • 变形金刚:变形金刚在变形之前是汽车,可以在陆地上移动。变形后还要可以说话,如果需要它还要可以变成飞机,在天空中飞翔

    image-20210624201212133

  • 多重加密系统:用户使用最简单的加密算法对字符串进行加密,如果觉得不够还可以二次加密、三次加密。提供逆向输出加密、求模加密等。

    image-20210624201344967

  • Java IO

    image-20210624201859462

    • 抽象构建类 InputStream
    • 具体构建类 FileInputStream ByteArrayStream等
    • 抽象装饰类 FilterInputStream
    • 具体装饰类 BufferedInputStream DataInputStream等

4.5 模式扩展

  • 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件

    image-20210624202110147

  • 透明装饰模式

    要求客户端完全针对抽象编程

    image-20210624202208061

  • 半透明装饰模式

    允许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法

    image-20210624202254333

5 外观模式 Facade

image-20210624202451375

5.1 模式动机

  • 引入外观角色之后,用户只需要直接与外观角色交互。
  • 用户与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦合度。

5.2 定义

  • 外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 外观模式又称为门面模式,它是一种对象结构型模式。

包含以下角色

  • Facade 外观角色
  • SubSystem 子系统角色

5.3 模式分析

  • “单一职责”:在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性
  • 为了使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口
  • “迪米特法则”:通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。
  • 客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。
  • 优点
    • 对客户端屏蔽子系统组件,简化代码
    • 实现了子系统和客户端的松耦合
    • 降低了大型软件中的编译依赖性,简化了系统在不同平台之间的移植过程
    • 提供了一个访问子系统的统一入口,不影响用户直接使用子系统类
  • 缺点
    • 不能很好地限制客户端使用子系统类
    • 在不引入抽象外观的前提下,增加新的子系统需要修改外观类或客户端代码,违背开闭原则
  • 适用
    • 为复杂子系统提供一个简单的接口时
    • 客户程序与多个子系统有依赖性时
    • 层次化结构中,可以使用外观模式定义系统每一层的入口,层与层之间不直接产生联系,而是通过外观类建立联系,降低层之间的耦合

5.4 例子

  • 电源总开关:为了使用方便,一个电源总开关可以控制四盏灯、一个风扇、一台空调和一台电视机的启动和关闭

    image-20210624202755875

  • 文件加密:加密流程分为三个操作,读取源文件、加密、保存加密后的文件。这三个操作相互独立,其业务代码封装在三个不同的类中

    image-20210624202905155

5.5 模式扩展

  • 单例

    通常只需要一个外观类,即单例模式

  • 抽象外观类

    在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题

    image-20210624203404002

6 享元模式 Flyweight

image-20210624203731084

6.1 模式动机

  • 对象数量太多时,运行代价高,带来性能下降问题
  • 可以共享的相同内容称为内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State)
  • 在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象
  • 一般设计成小对象,包含的内部状态少,也成为细粒度对象。目的就是使用共享技术来实现大量细粒度对象的复用。

6.2 定义

  • 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

包含以下角色:

  • Flyweight 抽象享元类
  • ConcreteFlyweight 具体享元类
  • UnsharedConcreteFlyweight 非功效具体享元类
  • FlyweightFactory 享元工厂类

6.3 模式分析

  • 考虑性能,通过享元来节约内存空间,提高系统性能

  • 核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池获取,如果存在则返回,如果不存在则创建后返回给用户,并存入享元池。

    image-20210624204724834

  • 关键在于区分内部状态(Internal State)和外部状态(External State)。

    • 内部状态是在享元对象内部,不会随环境变化
    • 外部状态是随环境改变而改变,不可以共享的状态
  • 优点

    • 极大减少内存中的对象数量
    • 外部状态相对独立,不会影响其内部状态,从而使得享元对象可以再不同的环境中被共享
  • 缺点

    • 使系统复杂,需要分离出内部对象和外部对象
    • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态的运行时间变长
  • 适用

    • 系统中有大量相同或相似的对象
    • 对象的大部分状态可以外部化,可以将这些外部状态传入对象中
    • 多次重复使用享元对象

6.4 例子

  • 共享网络设备(无外部状态)

    image-20210624204952529

  • 共享网络设备(有外部状态),分配给每一个终端计算机的端口不同,可以将端口从网络设备中抽取出来,需要时再进行设置

    image-20210624205106039

  • JDK 字符串常量池

6.5 模式扩展

  • 单纯享元模式

    所有的享元对象都是可以共享的,即所有的抽象享元类的子类都可以共享,不存在非共享具体享元类

    image-20210624205443816

  • 复合享元模式(联用组合模式)

    将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的享元对象本身不能共享,但是他们可以分解成单纯享元对象,后者可以共享

    image-20210624205553500

  • 联用简单工厂

    享元工厂通过提供一个静态的工厂方法用于返回享元对象

  • 联用单例模式

    通常只有唯一一个享元工厂

7 代理模式

image-20210624210141191

7.1 模式动机

  • 通过引入一个新的对象(如小图片和远程代理对象)来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式

7.2 定义

  • 代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式。

包含以下角色

  • Subject 抽象主题角色
  • Proxy 代理主题角色
  • RealSubject 真实主题角色

7.3 模式分析

  • 优点
    • 能够协调调用者和被调用者
    • 远程代理使得客户端可以访问在远程机器上的对象
    • 虚拟代理使用一个小对象来代表大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
    • 保护代理可以控制对真实对象的使用权限
  • 缺点
    • 在客户端和真实主题之间增加了代理对象,有些类型的代理模式可能造成请求的处理速度变慢
    • 实现代理模式需要额外的工作
  • 适用
    • 远程(Remote)代理:为一个位于不同的地址空间对象提供的一个本地代理对象
    • 虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象在被需要时才真正创建
    • Copy-on-Write代理:把复制操作延迟到只有在客户端真正需要时才执行,因为深克隆开销较大
    • 保护(Protect or Access)代理:控制一个对象的访问
    • 缓冲(Cache)代理:为一个目标操作提供临时存储空间,以便客户端共享这些结果
    • 防火墙(Firewall)代理:保护目标
    • 智能应用(Smart Reference)代理:提供一些额外操作,如增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。

7.4 例子

  • 论坛权限控制:已注册的用户可以发帖、修改信息等,而游客只可以浏览

    image-20210624210351723

  • 数学运算代理:访问远程实现的对象,本地进行调用

    image-20210624210443030

posted @ 2021-06-24 21:15  cpaulyz  阅读(205)  评论(0编辑  收藏  举报