设计模式之结构性模式

2024-01-28 05:30

本文主要是介绍设计模式之结构性模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 简介

在前面的博客中,自己分别为GOF23总结了设计模式之创建者模式和行为型模式,借着刚阅读完Java设计模式及实践,对辛格的阐述进行总结,期待可以增进对设计模式的理解。本文主要是对于辛格Java设计模式及实践中第四章结构性模式进行总结。

1.1 设计模式七大原则

在这里插入图片描述
其中依赖倒置原则(Dependence Inversion Principle)的范例如下:

母亲类有讲故事的方法TellStory,依赖一个Book类输入,并使用了Book类的getContent方法,以便讲故事。那么下次母亲类要讲报纸上的故事、手机上的故事原有接口无能为力。这时可以抽象一个包含getContent方法的IReader接口,Book,NewsPaper、CellPhone都实现该抽象接口,母亲类方法接受一个Ireader,并调用getContent方法即可。上述的例子的阐述较好。

在这里插入图片描述

参见设计模式七大原则总结

1.2 结构性模式的特征

结构性模式(Structural Pattern)利用对象和类之间的关系来创建复杂结构,大多数结构性模式都基于继承。可分为两种

  1. 类结构模式:关心类的组合,由多个类组合成一个更大的系统,在类结构模式中一般只存在于继承关系和实现关系。
  2. 对象结构性模式:关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。更符合“合成复用原则”

2 结构性模式类型

2.1 简述

2.2 逐个阐述

2.2.1 适配器模式

适配器提供了一种代码重用的方案,即将已有的代码适配或者包装到一些新接口中,而这些接口是在最初设计的时候没有考虑到的。
适配器的实现方式在其他语言中通常使用多继承的方式,也可以使用聚合法。
适配器模式的实现分为三类:

  1. 类适配器类型
    Adapter继承Adaptee,实现Target接口(因为没有多继承,Target必须是接口)
  2. 对象适配器类型
  3. 缺省适配器模式,也叫做默认适配器模式、接口适配器模式。
2.2.1.1 UML

在这里插入图片描述
上述图对象适配器模式(Object adapter pattern),与类适配器模式不同,Adapter只实现了Target的接口,没有继承Adaptee,而是使用聚合的方式引用adptee。

2.2.1.2 参与者
  1. Target
    定义Client使用的与特定领域相关的接口,即支持的新接口
  2. Client
    与符合Target接口的对象协同。
  3. Adaptee
    定义一个已经存在的接口,这个接口需要适配。
  4. Adapter
    对Adaptee的接口与Target接口进行适配
2.2.1.3 实践

参见 适配器模式
参见 GOF设计模式之适配器模式的理解
适配器把一个类的接口变换为客户端所期待的另一种接口,而使原来因接口原因不匹配而无法一起工作的两个类能够一起工作。

2.2.2 代理模式

代理模式的目的是为一个真实的对象提供一个代理对象。
适配器模式和代理模式之间的主要区别在于代理模式提供了完全相同的接口。装饰器模式增强了接口,而适配器更改了接口。

2.2.2.1 UML

在这里插入图片描述
上图为静态代理的UML。
在上图中,存在两个对象,一个是真实对象RealSubject,一个是ProxySubject代理对象,需要注意在代理对象中不存在业务逻辑,在建立句柄之后,会将业务请求委托给真实对象来处理,代理充当着中间人的角色。
在这里插入图片描述

在Java编程思想中p592页,则包含动态代理的阐述:
在这里插入图片描述

2.2.2.2 参与者
  1. Subject(共同接口):客户端使用的现有接口
  2. RealSubject(真实对象):真实对象的类
  3. ProxySubeject(代理对象):代理类。
2.2.2.3 实践

GOF23设计模式之动态代理模式之理解
GOF23设计模式之动态代理模式实践之经典
GOF23设计模式之静态代理模式实现之经典
GOF23 设计模式之静态代理模式理解之经典

2.2.3 装饰器模式

有时,在现有代码中添加或删除一些功能,同时对现有的代码结构不会造成影响,而这些添加或删除的功能又不足以做成一个子类。这种情况下装饰器模式就会排上用场。

装饰器模式聚合了它将要装饰的原有对象,实现了与原有对象相同的接口,代理微弱原有对象的所有公共接口调用,并且在子类中实现新增的功能,从而达到了上述的目的。

2.2.3.1 UML

在这里插入图片描述

2.2.3.2 参与者
  1. Componeent:抽象组件,可以是一个接口
  2. ComponentImplementation:想要装饰的组件之一。
  3. Decorator:这是一个抽象的组件装饰器。
  4. ExtendedComponent:这是添加额外功能的组件装饰器。
2.2.3.3 实践
package gof.structural.decorator;import java.util.stream.Collectors;
// 抽象的组件
interface PrintText {public void print(String text);
} 
// printASCIIText是要装饰的组件,它只知道打印ASCII文本
class PrintAsciiText implements PrintText {public void print(String text) {System.out.println("Print ASCII: " + text);}
} 
// printASCIIText是要装饰的组件,增强十六进制打印。使用了之前就有的功能 
class PrintTextHexDecorator implements PrintText {private PrintText inner;public PrintTextHexDecorator(PrintText inner) {this.inner = inner;}public void print(String text) {String hex = text.chars().boxed().map(x -> "0x" + Integer.toHexString(x)).collect(Collectors.joining(" "));
// 使用了之前就有的功能	inner.print(text + " -> HEX: " + hex);}
}public class Main
{public static void main (String[] args) throws java.lang.Exception{final String text = "text";final PrintText object = new PrintAsciiText();final PrintText printer = new PrintTextHexDecorator(object);object.print(text);printer.print(text);}
}

printTextHexDecorator是装饰器,它也可以应用于其他PrintText组件。假设需要一个PrintToUpperText,仍然可以使用现有的装饰器使其打印十六进制。
参见 GOF23设计模式之装饰模式之理解

装饰模式与桥接模式都是为了解决多子类对象问题,但是他们的诱因是不同的。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。而装饰器则是为了增加新的功能。

2.2.4 桥接模式

桥接模式的目标是将抽象与实现解耦,使得二者可以独立地变化。它通过在公共接口和视线中使用继承来达到目的。在处理跨平台开发时是非常常见的。
桥接模式的实现核心要点如下:

  • 处理多继承结构,处理多个维度变化的场景。
  • 将多个维度设计成独立变化的继承体系,使各个维度可以独立的进行扩展。
  • 在抽象层进行关联。
2.2.4.1 UML

在这里插入图片描述

2.2.4.2 参与者
  1. Abstraction:抽象类
  2. Implementation:抽象的实现类
  3. Refined:扩充的抽象类
  4. SpecificImplementation:具体实现类。
2.2.4.3 实践
package gof.structural.bridge;
// 平台继承体系
interface PlatformBridge {public void forwardMessage(String msg);
}class WindowsImplementation implements PlatformBridge {public void forwardMessage(String msg) {System.out.printf("Sending message \n%s \nFrom the windows machine", msg);}
}class PosixImplementation implements PlatformBridge {public void forwardMessage(String msg) {System.out.printf("Sending message \n%s \nFrom the linux machine", msg);}
}// 电子邮件客户端
class MessageSender {private PlatformBridge implementation;public MessageSender(PlatformBridge implementation) {this.implementation = implementation;}public void sendMessage(String from, String to, String body) {implementation.forwardMessage(String.format("From : %s \nTo : %s \nBody : %s", from, to, body));}
}class MyMessageClient extends MessageSender {private String to = "development_all@abc.com";public MyMessageClient(PlatformBridge implementation) {super(implementation);}public void sendMessageToAll(String from, String body) {sendMessage(from, to, body);}
}public class Main
{public static void main (String[] args){new MyMessageClient(new WindowsImplementation()).sendMessageToAll("abc@gmail.com", "Test");}
}

参见 GOF设计模式之桥接模式的实现

2.2.5 组合模式

组合模式是把一组对象组合为一个复杂的单一整体。JVM提供了组合模式的最佳示例,通常是利用堆栈的原理。各种操作从当前线程中推入和弹出。
使用模式的场景是:把部分和整体的关系使用树形结构来表示,从而使客户端可以使用统一的方式来处理部分对象和整体对象。组合模式与解释器模式很相似。因为解释器模式使用组合模式来定义对象结构的内部表示。解释器模式的UML图如下所示:
在这里插入图片描述

2.2.5.1 UML

在这里插入图片描述

2.2.5.2 参与者
  1. Component
    为组合中的对象声明接口。
    在适当的情况下,实现所有类共有接口的缺省行为。
    声明一个接口用于访问和管理Component的子组件。
    (可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
  2. Leaf
    在组合中表示叶节点对象,叶节点没有子节点。
    在组合中定义节点对象的行为。
  3. Composite
    定义有子部件的那些部件的行为。
    存储子部件。
    在Component接口中实现与子部件有关的操作。
  4. Client
    通过Component接口操纵组合部件的对象。
2.2.5.3 实践
package gof.structural.composite;
// 组合接口
interface ArithmethicComposite {public int getValue();
}
// 叶子节点
class NumericValue implements ArithmethicComposite {private int value;public NumericValue(int value) {this.value = value;}public int getValue() {return value;}
}// 复合节点
abstract class ArithmethicOperand implements ArithmethicComposite {protected ArithmethicComposite left;protected ArithmethicComposite right;public ArithmethicOperand(ArithmethicComposite left, ArithmethicComposite right) {this.left = left;this.right = right;}
}class PlusOperand extends ArithmethicOperand {public PlusOperand(ArithmethicComposite left, ArithmethicComposite right) {super(left, right);}public int getValue() {return left.getValue() + right.getValue();}
}class MinusOperand extends ArithmethicOperand {public MinusOperand(ArithmethicComposite left, ArithmethicComposite right) {super(left, right);}public int getValue() {return left.getValue() - right.getValue();}
}// 复合节点 逆波兰式计算。
public class Main
{public static void main (String[] args) throws java.lang.Exception{ArithmethicComposite expr = new MinusOperand(new PlusOperand(new NumericValue(1), new NumericValue(4)),new NumericValue(2));System.out.printf("Value equals %d\n", expr.getValue());}
}

另外参见GOF23设计模式之组合模式的理解
在阅读本书时,曾在组合模式结尾摘抄过如下的内容:
“慎勿放逸:放逸就是不去约束自己的行为,放纵自己为所欲为会给自己带来无穷无尽的烦恼和痛苦,其实很多祸害都不是你去做了一件事,而是因为你过度去做一件事。”因此知足常足,终身不辱,知止常止,终身不耻;如果能在该放行的时候放行,该止的时候止,不放纵自己的欲望,懂得控制自己的情绪,这同样也是一件积福积德的事。

2.2.6 外观模式

 许多复杂的系统可以简化为几个子系统暴露的用例接口,这样可以让客户端代码不需要知道子系统的内部结构与联系。即客户端代码和复杂的子系统解耦,并且能让开发人员更简单的使用子系统,这被称为外观模式Façade,外观模式隐藏了子系统的复杂内部结构,只向外部提供可访问的通用接口。
 目的是为复杂的子系统提供单一的统一接口。通过为最重要的用例提供接口,能够简化大型和复杂系统的使用。

2.2.6.1 UML

在这里插入图片描述

2.2.6.2 参与者
  1. Client:子系统客户端代码
  2. Façade:子系统接口
  3. Subsystem:子系统定义的类。
2.2.6.3 实践

GOF23设计模式之外观模式之实现

2.2.7 享元模式

对象池(创建者模式)模式与享元模式(结构模式)的区别在于:对象池模式是保存可变域的容器,二享元模式是不可变域对象。由于是不可变的,因此它们的状态是在创建时设置的,并且在每个方法调用时从外部给出外部状态。

抽象享元类通常是一个接口或者抽象类,声明公共方法,这些方法可以向外界提供内部状态,设置外部状态。围棋类棋子使用享元模式。

2.2.7.1 UML类图

在这里插入图片描述
在这里插入图片描述

2.2.7.2 参与者
  1. Flyweight
    描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。
  2. ConcreteFlyweight
    实现Flyweight接口,并为内部状态(如果有的话)增加存储空间。
    ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的;即,它必须独立于ConcreteFlyweight对象的场景。
  3. UnsharedConcreteFlyweight
    并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。
    在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
  4. FlyweightFactory
    创建并管理flyweight对象。
    确保合理地共享flyweight。当用户请求一个flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。
2.2.7.3 实践

参见 GOF23 设计模式之享元模式的实现

3 总结

在这里插入图片描述
结构型设计模式的主要内容就如上所示。代理模式、适配器模式、组合模式、装饰器模式在日常工作中较为经常使用。若想要用的纯熟,还是需要在日常工作编程中多多实践、揣摩反思这些用法的意义。
最后,附上完整的脑图:
在这里插入图片描述

																	2019-12-22 14:50于马塍路36

这篇关于设计模式之结构性模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/652599

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

软件架构模式:5 分钟阅读

原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统