设计模式之结构型模式-对比

结构型模式

结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

  • 适配器模式

    • 让接口不兼容的对象能够相互合作。
  • 桥接模式

    • 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。
  • 组合模式

    • 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。
  • 装饰模式

    • 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
  • 外观模式

    • 能为程序库、 框架或其他复杂类提供一个简单的接口。
  • 享元模式

    • 摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
  • 代理模式

    • 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

比较

名称适配器模式桥接模式组合模式装饰模式外观模式享元模式代理模式
英文名称亦称: 封装器模式、Wrapper、Adapter亦称: Bridge亦称: 对象树、Object Tree、Composite亦称: 装饰者模式、装饰器模式、Wrapper、Decorator亦称: 门面模式、Facade亦称: 缓存、Cache、Flyweight亦称: Proxy
意图适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。

装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
适合应用场景当你希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用适配器类。

适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。

如果您需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。

你可以扩展每个子类, 将缺少的功能添加到新的子类中。 但是, 你必须在所有新子类中重复添加这些代码, 这样会使得代码有坏味道。

将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。 然后你可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。
如果你想要拆分或重组一个具有多重功能的庞杂类 (例如能与多个数据库服务器进行交互的类), 可以使用桥接模式。

类的代码行数越多, 弄清其运作方式就越困难, 对其进行修改所花费的时间就越长。 一个功能上的变化可能需要在整个类范围内进行修改, 而且常常会产生错误, 甚至还会有一些严重的副作用。

桥接模式可以将庞杂类拆分为几个类层次结构。 此后, 你可以修改任意一个类层次结构而不会影响到其他类层次结构。 这种方法可以简化代码的维护工作, 并将修改已有代码的风险降到最低。

如果你希望在几个独立维度上扩展一个类, 可使用该模式。

桥接建议将每个维度抽取为独立的类层次。 初始类将相关工作委派给属于对应类层次的对象, 无需自己完成所有工作。

如果你需要在运行时切换不同实现方法, 可使用桥接模式。

当然并不是说一定要实现这一点, 桥接模式可替换抽象部分中的实现对象, 具体操作就和给成员变量赋新值一样简单。

顺便提一句, 最后一点是很多人混淆桥接模式和策略模式的主要原因。 记住, 设计模式并不仅是一种对类进行组织的方式, 它还能用于沟通意图和解决问题。
如果你需要实现树状对象结构, 可以使用组合模式。

组合模式为你提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。

如果你希望客户端代码以相同方式处理简单和复杂元素, 可以使用该模式。

组合模式中定义的所有元素共用同一个接口。 在这一接口的帮助下, 客户端不必在意其所使用的对象的具体类。
如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。

装饰能将业务逻辑组织为层次结构, 你可为各层创建一个装饰, 在运行时将各种不同逻辑组合成对象。 由于这些对象都遵循通用接口, 客户端代码能以相同的方式使用这些对象。

如果用继承来扩展对象行为的方案难以实现或者根本不可行, 你可以使用该模式。

许多编程语言使用 final最终关键字来限制对某个类的进一步扩展。 复用最终类已有行为的唯一方法是使用装饰模式: 用封装器对其进行封装。
如果你需要一个指向复杂子系统的直接接口, 且该接口的功能有限, 则可以使用外观模式。

子系统通常会随着时间的推进变得越来越复杂。 即便是应用了设计模式, 通常你也会创建更多的类。 尽管在多种情形中子系统可能是更灵活或易于复用的, 但其所需的配置和样板代码数量将会增长得更快。 为了解决这个问题, 外观将会提供指向子系统中最常用功能的快捷方式, 能够满足客户端的大部分需求。

如果需要将子系统组织为多层结构, 可以使用外观。

创建外观来定义子系统中各层次的入口。 你可以要求子系统仅使用外观来进行交互, 以减少子系统之间的耦合。

让我们回到视频转换框架的例子。 该框架可以拆分为两个层次: 音频相关和视频相关。 你可以为每个层次创建一个外观, 然后要求各层的类必须通过这些外观进行交互。 这种方式看上去与中介者模式非常相似。
仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式。

应用该模式所获的收益大小取决于使用它的方式和情景。 它在下列情况中最有效:

程序需要生成数量巨大的相似对象
这将耗尽目标设备的所有内存
对象中包含可抽取且能在多个对象间共享的重复状态。
使用代理模式的方式多种多样, 我们来看看最常见的几种。

延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。

你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。

访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。

代理可仅在客户端凭据满足要求时将请求传递给服务对象。

本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。

在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。

记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。

缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。

代理可对重复请求所需的相同结果进行缓存, 还可使用请求参数作为索引缓存的键值。
智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。

代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。

代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。
模式优点_单一职责原则_你可以将接口或数据转换代码从程序主要业务逻辑中分离。
开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
你可以创建与平台无关的类和程序。
客户端代码仅与高层抽象部分进行互动, 不会接触到平台的详细信息。
开闭原则。 你可以新增抽象部分和实现部分, 且它们之间不会相互影响。
单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
你可以利用多态和递归机制更方便地使用复杂树结构。
开闭原则。 无需更改现有代码, 你就可以在应用中添加新元素, 使其成为对象树的一部分。
你无需创建新子类即可扩展对象的行为。
你可以在运行时添加或删除对象的功能。
你可以用多个装饰封装对象来组合几种行为。
单一职责原则。 你可以将实现了许多不同行为的一个大类拆分为多个较小的类。
你可以让自己的代码独立于复杂子系统。如果程序中有很多相似对象, 那么你将可以节省大量内存你可以在客户端毫无察觉的情况下控制服务对象。
如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。
即使服务对象还未准备好或不存在, 代理也可以正常工作。
开闭原则。 你可以在不对服务或客户端做出修改的情况下创建新代理。
模式缺点代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。对高内聚的类使用该模式可能会让代码更加复杂。对于功能差异较大的类, 提供公共接口或许会有困难。 在特定情况下, 你需要过度一般化组件接口, 使其变得令人难以理解。在封装器栈中删除特定封装器比较困难。
实现行为不受装饰栈顺序影响的装饰比较困难。
各层的初始化配置代码看上去可能会很糟糕。
外观可能成为与程序中所有类都耦合的上帝对象。你可能需要牺牲执行速度来换取内存, 因为他人每次调用享元方法时都需要重新计算部分情景数据。
代码会变得更加复杂。 团队中的新成员总是会问: ​ “为什么要像这样拆分一个实体的状态?”。
代码可能会变得复杂, 因为需要新建许多类。
服务响应可能会延迟。
说明使接口不兼容的对象能够相互合作多维度扩展时由继承改为组合,
抽象对象负责交互,实现部分负责处理{数据库驱动}
将程序各个部分独立开发
将对象组合成树状结构添加新行为,增强行为 ,穿衣服简化的接口:接线员就是该商店的所有服务和部门的外观缓存,共享请求提交给对象前后进行一些处理。
Java 常用设计模式让接口不兼容的对象能够相互合作。可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。能为程序库、 框架或其他复杂类提供一个简单的接口。摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理

何时使用?

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值