设计模式之结构型

大家好,这里是编程Cookbook,关注公众号「编程Cookbook」,获取更多面试资料。本文是对设计模式中创建模式的详细讲解,共7种,分别是适配器模式、桥接模式、组合模式、装饰器模式、代理模式、外观模式、享元模式。



常用结构型模式

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

适配器模式(Adapter Pattern)

适配器模式是一种结构型设计模式,它允许将一个类的接口转换成另一个接口。适配器模式的核心思想是解决接口不兼容的问题,使得原本由于接口不匹配而无法一起工作的类可以协同工作。

组成成分

适配器模式通常包含以下角色:

  1. 目标接口(Target)

    • 客户端期望的接口,定义了客户端需要使用的功能。
  2. 适配者(Adaptee)

    • 需要被适配的类,通常是已经存在的类,但其接口与目标接口不兼容
  3. 适配器(Adapter)

    • 适配器的核心类,负责将适配者的接口转换成目标接口
    • 适配器可以是 类适配器(通过继承实现)或 对象适配器(通过组合实现,后面会讲解组合模式)。

特点

优点
  1. 解耦:将客户端与适配者解耦,客户端只需要依赖目标接口。
  2. 复用性:可以复用现有的类,无需修改其代码。
  3. 灵活性:可以动态适配不同的适配者。
缺点
  1. 复杂度增加:引入了额外的适配器类,增加了系统的复杂度。
  2. 过度使用问题:如果系统中大量使用适配器,可能会导致代码难以理解和维护。

使用场景

  1. 接口不兼容

    • 当需要使用一个已经存在的类,但其接口与系统要求的接口不匹配时。
  2. 复用现有类

    • 当需要复用一些现有的类,但这些类的接口不符合系统的需求时。
  3. 统一接口

    • 当需要统一多个类的接口,以便客户端可以统一调用时。
  4. 第三方库集成

    • 当需要集成第三方库或组件,但其接口与系统不兼容时。

实现方式

1. 类适配器
  • 定义:通过继承适配者类来实现适配器。
  • 特点:适配器类既是目标接口的实现类,又是适配者类的子类。
  • 适用场景:适配者类的方法可以直接被复用。
2. 对象适配器
  • 定义:通过组合适配者对象来实现适配器。
  • 特点:适配器类持有适配者对象的引用,并通过调用适配者对象的方法来实现目标接口。
  • 适用场景:适配者类的方法不能直接复用,或者需要适配多个适配者类。

例子

  • 场景:你有一个旧的打印机类(OldPrinter),它有一个 printDocument() 方法,但你的新系统需要一个 print() 方法。
  • 解决方案:创建一个适配器类,将 printDocument() 方法适配为 print() 方法。
  • 应用:在集成第三方库或旧系统时,适配器模式非常有用。

总结

  • 适配器模式通过转换接口,使得不兼容的类可以协同工作,适用于接口不兼容、复用现有类或集成第三方库的场景。
  • 适配器模式分为类适配器对象适配器,前者通过继承实现,后者通过组合实现。
  • 适配器模式能够有效提升系统的灵活性和复用性,但需要注意避免过度使用,以免增加系统复杂度。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

桥接模式(Bridge Pattern)

桥接模式是一种结构型设计模式,它将抽象部分实现部分 分离,使它们可以独立变化(通过组合方式连接抽象实现)。桥接模式的核心思想是通过组合代替继承,从而避免类爆炸问题,并提高系统的灵活性和可扩展性。

组成成分

桥接模式通常包含以下角色:

  1. 抽象部分(Abstraction)

    • 定义抽象接口,通常包含对实现部分的引用
    • 可以是抽象类或接口。
  2. 扩展抽象部分(Refined Abstraction)

    • 对抽象部分的扩展,提供更具体的功能
  3. 实现部分(Implementor)

    • 定义实现类的接口,通常是一个抽象类或接口。
  4. 具体实现部分(Concrete Implementor)

    • 实现 实现部分的接口,提供具体的实现。

特点

优点
  1. 解耦抽象与实现:抽象部分和实现部分可以独立变化,互不影响。
  2. 提高扩展性:可以独立扩展抽象部分和实现部分,避免类爆炸问题。
  3. 符合开闭原则:新增抽象或实现时,无需修改现有代码。
缺点
  1. 复杂度增加:引入了更多的类和接口,增加了系统的复杂度。
  2. 设计难度较高:需要正确识别抽象部分和实现部分,设计不当可能导致系统难以理解。

使用场景

  1. 抽象与实现需要独立变化

    • 当抽象部分和实现部分需要独立扩展时,可以使用桥接模式。
  2. 避免类爆炸

    • 当使用继承会导致类层次结构过于复杂时,可以使用桥接模式。

    如果直接使用继承,当每个维度有多个变化时,可能会导致子类数量呈指数增长。

  3. 跨平台开发

    • 当需要开发跨平台应用时,可以使用桥接模式将平台相关代码与业务逻辑分离。

例子

  • 场景:你有一个图形绘制系统,支持多种形状(如圆形、矩形)和多种颜色(如红色、蓝色)。
  • 解决方案:将形状和颜色分离,通过桥接模式组合它们,避免类数量爆炸
  • 应用:适用于多个维度变化的场景,如 GUI 库中的控件和主题。

总结

  • 桥接模式通过将抽象部分与实现部分分离,使它们可以独立变化,适用于抽象与实现需要独立扩展的场景。
  • 桥接模式能够有效避免类爆炸问题,并提高系统的灵活性和可扩展性。
  • 实现时需要注意正确识别抽象部分和实现部分,避免设计不当导致系统复杂度增加。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

组合模式(Composite Pattern)

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构表示“部分-整体”的层次结构。组合模式的核心思想是统一对待单个对象和组合对象,使得客户端可以一致地处理它们。

组成成分

组合模式通常包含以下角色:

  1. 组件(Component)

    • 定义所有对象的通用接口,可以是抽象类或接口。
    • 声明了组合对象和叶子对象的共同操作。
  2. 叶子(Leaf)

    • 表示树形结构中的叶子节点,没有子节点。
    • 实现了组件接口的具体操作。
  3. 组合(Composite)

    • 表示树形结构中的分支节点,包含子节点。
    • 实现了组件接口,并管理子节点(添加、删除、遍历等)。

特点

优点
  1. 简化客户端代码:客户端不需要关心处理的是单个对象还是组合对象。
  2. 易于扩展:可以很容易地增加新的组件类型。
缺点
  1. 设计复杂度增加:需要正确设计组件接口,确保所有组件类型都能实现。
  2. 类型检查问题:在某些语言中,可能需要运行时类型检查来确保操作的安全性。

使用场景

  1. 树形结构

    • 当需要表示对象的“部分-整体”层次结构时,如文件系统、组织架构等。
  2. 统一处理

    • 希望客户端能够一致地处理单个对象和组合对象时。
  3. 递归结构

    • 当需要处理递归结构时,如菜单、目录等。
  4. 动态组合

    • 当需要在运行时动态组合对象时。

例子

  • 场景:你需要处理文件系统中的文件和文件夹。文件夹可以包含文件或其他文件夹。
  • 解决方案:使用组合模式,将文件和文件夹统一为“组件”,用户可以递归地操作整个结构
  • 应用:适用于树形结构数据的场景,如组织机构、菜单系统等。

总结

  • 组合模式通过将对象组合成树形结构,使得客户端可以一致地处理单个对象和组合对象,适用于表示“部分-整体”层次结构的场景。
  • 组合模式能够简化客户端代码,并提高系统的灵活性和可扩展性。
  • 实现时需要注意正确设计组件接口,确保所有组件类型都能实现,并处理好递归结构。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

装饰器模式(Decorator Pattern)

装饰器模式是一种结构型设计模式,它允许动态地为对象添加职责,而无需修改其代码。装饰器模式的核心思想是通过组合代替继承,从而在不改变对象结构的情况下扩展其功能

组成成分

装饰器模式通常包含以下角色:

  1. 组件接口(Component)

    • 定义所有对象的通用接口,可以是抽象类或接口。
  2. 具体组件(Concrete Component)

    • 实现组件接口的具体类,是被装饰的原始对象
  3. 装饰器(Decorator)

    • 持有组件接口的引用,并实现组件接口
    • 可以在调用组件接口的方法前后添加额外的行为
  4. 具体装饰器(Concrete Decorator)

    • 实现装饰器接口,为组件添加具体的职责

特点

优点
  1. 动态扩展:可以在运行时动态地为对象添加职责。
  2. 符合开闭原则:新增功能时无需修改现有代码。
  3. 灵活性高:可以通过组合不同的装饰器来实现复杂的功能。
缺点
  1. 复杂度增加:引入了更多的类和对象,增加了系统的复杂度。
  2. 设计难度较高:需要正确设计装饰器接口,确保所有装饰器都能正确扩展组件。

使用场景

  1. 动态扩展功能

    • 当需要动态地为对象添加功能时,可以使用装饰器模式。
  2. 避免类爆炸

    • 当使用继承会导致类层次结构过于复杂时,可以使用装饰器模式。
  3. 不可修改的类

    • 当需要扩展一个不可修改的类时,可以使用装饰器模式。
  4. 多重功能组合

    • 当需要组合多个功能时,可以使用装饰器模式。

例子

  • 场景:你有一个咖啡类,需要动态地添加配料(如糖、牛奶)
  • 解决方案:使用装饰器模式,将配料作为装饰器类,动态地添加到咖啡对象中。
  • 应用:适用于需要动态扩展对象功能的场景,如 Java 的 I/O 流。

总结

  • 装饰器模式通过动态地为对象添加职责,使得系统更加灵活和可扩展,适用于需要动态扩展功能的场景。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

代理模式(Proxy Pattern)

代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式的核心思想是通过代理对象间接访问目标对象,从而可以在访问目标对象时添加额外的控制逻辑

代理模式的类型

  1. 静态代理

    • 编译时创建代理类,代理类与目标类实现相同的接口。
  2. 动态代理

    • 运行时动态生成代理类,适用于无需事先定义代理类的场景。

特点

优点
  1. 控制访问:可以在访问目标对象时添加额外的控制逻辑。
  2. 解耦:将客户端与目标对象解耦,客户端只需要与代理交互。
  3. 增强功能:可以在不修改目标对象的情况下增强其功能。
缺点
  1. 复杂度增加:引入了额外的代理类,增加了系统的复杂度。
  2. 性能开销:代理模式可能会引入额外的性能开销。

使用场景

以下是代理模式使用场景的简要介绍:

  1. 远程访问
    当客户端需要访问位于远程的对象时,使用远程代理。远程代理处理与远程对象的通信细节,对客户端来说就像在访问本地对象一样。

  2. 延迟加载
    对于创建开销大的对象,采用虚拟代理实现延迟加载。在真正需要使用对象之前,虚拟代理代替真实对象占位,实际使用时才创建真实对象,避免不必要的资源消耗。

  3. 权限控制
    若要控制对某些敏感对象的访问权限,可利用保护代理。保护代理根据请求方的权限决定是否允许其访问对象,保障资源安全。

  4. 智能引用
    当希望在访问对象时执行额外操作,例如对象访问计数、缓存机制、资源清理等,智能引用代理就可派上用场。它在对象访问过程中实现这些附加功能。

例子

  • 场景:你需要加载网络图片,但图片加载较慢,希望在加载完成前显示占位符。
  • 解决方案:使用代理模式,代理对象在图片加载完成前显示占位符,加载完成后替换为真实图片。
  • 应用:适用于远程代理、虚拟代理、保护代理等场景。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

适配器模式、桥接模式、组合模式、装饰器模式、代理模式的对比

模式核心思想适用场景主要区别
适配器模式将一个类的接口转换成客户端期望的另一个接口。接口不兼容,需要复用现有类。通过适配器转换接口,解决接口不兼容问题。
桥接模式将抽象部分与实现部分分离,使它们可以独立变化。抽象与实现需要独立变化,避免类爆炸。通过分离抽象与实现,使它们可以独立扩展。
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。需要处理树形结构的数据。通过树形结构统一处理单个对象和组合对象。
装饰器模式动态地为对象添加职责,不改变其接口。需要动态扩展对象功能。通过组合扩展对象功能,不改变接口。
代理模式为其他对象提供一种代理以控制对这个对象的访问。需要控制对象的访问或延迟加载。通过代理控制对象的访问,通常不改变接口。

总结

  • 适配器模式:解决接口不兼容问题,适用于复用现有类。
  • 桥接模式:分离抽象与实现,适用于抽象与实现需要独立变化的场景。
  • 组合模式:处理树形结构,适用于“部分-整体”层次结构的场景。
  • 装饰器模式:动态扩展对象功能,适用于需要动态添加职责的场景。
  • 代理模式:控制对象访问,适用于需要延迟加载、权限控制等场景。

每种模式都有其特定的应用场景和核心思想,理解它们的区别有助于在实际开发中正确选择和使用设计模式。

非常用结构型模式

外观模式(Facade Pattern)

外观模式是一种结构型设计模式,旨在为复杂的子系统提供一个简化的接口。通过引入一个外观类,客户端只需与外观类交互,而无需直接与子系统的多个类打交道。

组成成分

  1. 外观类(Facade):定义一个更高级别的接口,用于封装对子系统的访问。
  2. 子系统类(Subsystem Classes):实现系统的具体功能,通过外观类对外暴露。

特点

  • 简化接口:外观模式通过提供一个简化的接口,隐藏了系统的复杂性,使得客户端更容易使用。
  • 解耦:客户端与子系统之间的耦合度降低,子系统的变化不会直接影响客户端。
  • 灵活性:可以在不影响客户端的情况下修改子系统的内部实现。

适用场景

  • 当需要为一个复杂的子系统提供一个简单的接口时。
  • 当客户端与子系统之间存在大量依赖关系,且希望减少这种依赖时。
  • 当需要将子系统分层时,外观模式可以作为每层的入口点。

例子

  • 场景:你需要操作电脑硬件(如 CPU、硬盘、内存),但直接调用这些硬件的接口很复杂。
  • 解决方案:使用外观模式,提供一个统一的接口(如操作系统)来简化硬件操作。
  • 应用:适用于简化复杂系统调用的场景。

总结

  • 外观模式:用于简化复杂系统的接口,提供一个统一的入口点,适用于需要隐藏系统复杂性的场景。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

享元模式(Flyweight Pattern)

享元模式是一种结构型设计模式,旨在通过共享尽可能多的相似对象来减少内存使用或计算开销。它适用于大量对象且这些对象有相似状态的情况。

组成成分

  1. 享元接口(Flyweight Interface):定义对象的通用接口,用于操作共享的内部状态。
  2. 具体享元类(Concrete Flyweight):实现享元接口,并存储内部状态。内部状态是共享的,不会随外部环境变化。
  3. 享元工厂(Flyweight Factory):创建并管理享元对象,确保共享对象的唯一性。
  4. 客户端(Client):维护外部状态,并在需要时将外部状态传递给享元对象。定义不需要共享的状态或对象。

特点

  • 共享对象:享元模式通过共享相似对象来减少内存占用。
  • 分离内部状态和外部状态:内部状态是共享的,外部状态由客户端维护并传递给享元对象。
  • 提高性能:通过减少对象数量,享元模式可以显著提高性能,特别是在处理大量对象时。

适用场景

  • 当一个应用程序使用了大量相似对象,且这些对象的大部分状态可以外部化时。
  • 当对象的大部分状态可以外部化,且可以通过共享来减少内存占用时。
  • 当需要缓存或池化对象以提高性能时。

例子

  • 场景:你在开发一个游戏,需要创建大量树木对象,但这些对象的状态(如纹理、颜色)大部分是相同的
  • 解决方案:使用享元模式,将树木的共享状态(如纹理)提取出来,减少内存占用。
  • 应用:适用于存在大量相似对象的场景,如游戏中的棋子、字符等。

总结

  • 享元模式:用于优化大量相似对象的内存使用,通过共享对象来减少资源消耗,适用于需要处理大量相似对象的场景。

关注公众号「编程Cookbook」,获取更多编程学习/面试资料!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值