《图解设计模式》笔记(一)适应设计模式

图灵社区 - 图解设计模式 - 随书下载

评论区

雨帆 2017-01-11 16:14:04

对于设计模式,我个人认为,其实代码和设计原则才是最好的老师。理解了 SOLID,如何 SOLID,自然而然地就用起来设计模式了。Github 上有一个 tdd-training,里面就是教你如何设计重构的。换句话说,此书可能不是很推荐。

设计模式的学习还是要靠 kata 练习

https://github.com/Pragmatists/tdd-trainings

一、Iterator模式:一个一个遍历

示例程序类图

在这里插入图片描述

public static void main(String[] args) {BookShelf bookShelf = new BookShelf(4);bookShelf.appendBook(new Book("Around the World in 80 Days"));bookShelf.appendBook(new Book("Bible"));bookShelf.appendBook(new Book("Cinderella"));bookShelf.appendBook(new Book("Daddy-Long-Legs"));Iterator it = bookShelf.iterator();while (it.hasNext()) {Book book = (Book)it.next();System.out.println(book.getName());}
}

角色

在这里插入图片描述

  • Iterator(迭代器)

    定义按顺序逐个遍历元素的接口(APl)。

    在示例程序中,Iterator接口扮演这个角色,它定义了两个方法:hasNext(判断是否存在下一个元素)和next(获取该元素)。

  • Concretelterator(具体的迭代器)

    负责实现Iterator角色所定义的接口(API)。

    在示例程序中,BookShelfIterator类扮演这个角色。该角色中包含了遍历集合所必需的信息。

    在示例程序中,BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。

  • Aggregate(集合)

    负责定义创建Iterator角色的接口(API)。这个接口(API)是一个方法,会创建出”按顺序访问保存在我内部元素的人“。

    在示例程序中,Aggregate接口扮演这个角色,它里面定义了iterator方法。

  • ConcreteAggregate(具体的集合)

    负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。

    在示例程序中,由BookShelf类扮演这个角色,它实现了iterator方法

扩展思路的要点

不管实现如何变化,都可以使用lterator

不用for循环,而使用Iterator模式的一个重要的理由:引入Iterator后可以将遍历与实现分离开来。

请看下面的代码。

while (it.hasNext()) {Book book =(Book)it.next();System.out.println(book.getName());
}

这里只使用了IteratorhasNext()next(),并没有调用BookShelf的方法。

即:这里的while循环并不依赖于BookShelf的实现。

那么管理书本就可以不用数组,可以换成java.util.VectorArrayList等别的形式。

不管BookShelf如何变化,只要BookShelf的iterator方法能正确地返回Iterator的实例就行,对于BookShelf的调用者很友好。

设计模式的作用就是帮助我们编写可复用的类。

所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需要很小的修改即可应对。

难以理解抽象类和接口

不要只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。

为了弱化类之间的耦合,进而使得类更加容易作为组件被再次利用,我们需要引入抽象类和接口。

Aggregate 和 Iterator 的对应

如何把BookShelfIterator类定义为BookShelf类的Concretelterator角色的:BookShelfIterator类知道BookShelf是如何实现的。因此,我们才能调用用来获取下一本书的getBookAt方法。

也就是说,如果BookShelf的实现发生了改变,即getBookAt方法这个接口(API)发生变化时,必须修改BookShelfIterator类。

正如Aggregate和Iterator这两个接口对应的一样,concreteAggregate和ConcreteIterator这两个类也是对应的。

多个 Iterator

“将遍历功能置于Aggregate角色之外”是Iterator模式的一个特征。根据这个特征,可以针对一个ConcreteAggregate角色编写多个Concretelterator角色。

迭代器的种类多种多样

在示例程序中展示的Iterator类只是很简单地从前向后遍历集合。可以改成从后向前、双向遍历、根据条件跳跃式遍历等。

不需要 deletelterator

在Java中,没有被使用的对象实例将会自动被删除(垃圾回收,GC)。因此,在iterator中不需要与其对应的deleteIterator方法。

相关的设计模式

Visitor模式(第13章)

Iterator模式是从集合中逐个取出元素进行遍历,但并没有在Iterator接口中声明对取出的元素进行何种处理。

Visitor模式则是在遍历元素集合的过程中,对元素进行相同的处理。

Composite模式(第11章)

Composite模式是具有递归结构的模式,在其中使用Iterator模式比较困难。

Factory Method模式(第4章)

在iterator方法中生成Iterator的实例时可能会使用Factory Method模式。

二、Adapter模式:加个“适配器”以便于复用

示例程序类图

继承的方式

在这里插入图片描述

委托的方式

在这里插入图片描述

public static void main(String[] args) {Print p = new PrintBanner("Hello");p.printWeak();p.printStrong();
}

角色

在这里插入图片描述

  • Target(对象)

    负责定义所需的方法。

    类比让笔记本电脑正常工作所需的直流12伏特电源。

    在示例程序中,由Print接口(使用继承时)和Print类(使用委托时)扮演此角色。

  • Client(请求者)

    负责使用Target 角色所定义的方法进行具体处理。

    类比直流12伏特电源所驱动的笔记本电脑。

    在示例程序中,由Main类扮演此角色。

  • Adaptee(被适配)

    注意不是Adapt-er(适配)角色,而是Adapt-ee(被适配)角色。

    Adaptee是一个持有既定方法的角色。

    类比交流220伏特电源。

    在示例程序中,由Banner类扮演此角色。

    如果Adaptee角色中的方法与Target角色的方法相同(也就是说家庭使用的电压就是12伏特直流电压),就不需要接下来的Adapter角色了。

  • Adapter(适配)

    Adapter模式的主人公。使用Adaptee角色的方法来满足Target角色的需求,这是Adapter模式的目的,也是Adapter角色的作用。

    类比将交流100伏特电压转换为直流12伏特电压的适配器。

    在示例程序中,由PrintBanner类扮演这个角色。

    在类适配器模式中,Adapter角色通过继承来使用Adaptee角色,而在对象适配器模式中,Adapter角色通过委托来使用Adaptee角色。

拓展思路的要点

什么时候使用Adapter模式

如果某个方法就是我们所需要的方法,那么直接在程序中使用不就可以了吗?为什么还要考虑使用Adapter模式呢?

很多时候,我们并非从零开始编程,经常会用到现有的类。特别是当现有的类已经被充分测试过了,Bug很少,而且已经被用于其他软件之中时,我们更愿意将这些类作为组件重复利用。

Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。

当出现Bug时,由于我们很明确地知道Bug不在现有的类(Adaptee角色)中,所以只需调查扮演Adapter角色的类即可,方便排查代码问题。

如果没有现成的代码

使用Adapter模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口(API)。

此外,在Adapter模式中,并非一定需要现成的代码。只要知道现有类的功能,就可以编写出新的类。

版本升级与兼容性

版本升级时常会出现“与旧版本的兼容性”问题。现实中往往很难完全抛弃旧版本。

这时,可以使用Adapter模式使新旧版本兼容,以便同时维护新版本和旧版本。

例如,假设我们今后只想维护新版本。这时可以让新版本扮演Adaptee角色,旧版本扮演Target角色。接着编写一个扮演Adapter角色的类,让它使用新版本的类来实现旧版本的类中的方法。

在这里插入图片描述

功能完全不同的类

当然,当Adaptee角色和Target角色的功能完全不同时,Adapter模式是无法使用的。就如同我们无法用交流100伏特电压让自来水管出水一样。

相关的设计模式

Bridge模式(第9章)

Adapter模式用于连接接口(API)不同的类,而Bridge模式则用于连接类的功能层次结构与实现层次结构。

Decorator模式(第12章)

Adapter模式用于填补不同接口(API)之间的缝隙,而 Decorator模式则是在不改变接口(API)的前提下增加功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/261797.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

科技快讯:鸿道Intewell操作系统突破国际垄断,引领工控新革命

科东软件Intewell鸿道工业操作系统,已在多种严苛环境下运行检验,并应用于工业控制、智能制造、汽车电子、轨道交通、能源电力、航天航空等实时性要求极高的领域,历经30年的不懈努力和研发迭代,在功能和性能上可以替代风和VxWorks操…

RTP 实时运输协议

目录 1 实时运输协议 RTP 1.1 RTP 的层次 1.2 RTP 分组的首部格式 2 实时运输控制协议 RTCP 2.1 RTCP 分组 1 实时运输协议 RTP 实时运输协议 RTP (Real-time Transport Protocol) 为实时应用提供端到端的运输,但不提供任何服务质量的保证。 需要发送的多媒体…

【Linux系统化学习】深入理解匿名管道(pipe)和命名管道(fifo)

目录 进程间通信 进程间通信目的 进程间通信的方式 管道 System V IPC(本地通信) POSIX IPC(网络通信) 管道 什么是管道 匿名管道 匿名管道的创建 匿名管道的使用 匿名管道的四种情况 匿名管道的五种特性 命名管道 …

瑞_Redis_初识Redis(含安装教程)

文章目录 1 初识Redis1.1 认识NoSQL1.1.1 结构化与非结构化1.1.2 关联和非关联1.1.3 查询方式1.1.4 事务1.1.5 总结 1.2 认识Redis1.2.1 介绍1.2.2 特征1.2.3 优势 1.3 安装Redis ★★★1.3.1 Linux安装Redis1.3.1.1 安装Redis依赖 1.3.2 Windows安装Redis1.3.2.1 安装步骤1.3.…

抛弃chatgpt,使用微软的Cursor提升coding效率

Whats Cursor? Cursor编辑器是一个基于GPT-4的代码编辑器,它可以根据用户的自然语言指令或者正在编辑的代码上下文为用户提供代码建议,支持多种编程语言,如Python、Java、C/C#、go等。Cursor编辑器还可以帮助用户重构、理解和优化代码&…

Code Control Process

代码提交流程(Code Control Process) VSS,早前定义的版本控制,没有谁对不对,但是要根本解决冲突,特别人多的时候,50个人的时候,处理冲突时非常的麻烦的,改半天还改错了&…

vulfocus靶场搭建

vulfocus靶场搭建 什么是vulfocus搭建教程靶场配置场景靶场编排靶场优化 什么是vulfocus Vulfocus 是一个漏洞集成平台,将漏洞环境 docker 镜像,放入即可使用,开箱即用,我们可以通过搭建该靶场,简单方便地复现一些框架…

Java面试题之分布式/微服务篇

经济依旧不景气啊,如此大环境下Java还是这么卷,又是一年一次的金三银四。 兄弟们,你准备好了吗?冲冲冲!欧里给! 分布式/微服务相关面试题解 题一:CAP理论,BASE理论题二:…

企业电子招投标系统源码之电子招投标系统建设的重点和未来趋势

功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看所…

如何使用Docker部署MongoDB并结合内网穿透实现远程访问本地数据库

文章目录 前言1. 安装Docker2. 使用Docker拉取MongoDB镜像3. 创建并启动MongoDB容器4. 本地连接测试5. 公网远程访问本地MongoDB容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 正文开始前给大家推荐个网站,前些天发现了一个巨牛的 …

SICTF Round#3 の WP

Misc 签到 SICTF{1f4ce05a-0fed-42dc-9510-6e76dff8ff53} Crypto [签到]Vigenere 附件内容: Gn taj xirly gf Fxgjuakd, oe igywnd mt tegbs mnrxxlrivywd sngearbsw wakksre. Bs kpimj gf tank, it bx gur bslenmngn th jfdetagur mt ceei yze Ugnled Lystel t…

mysql 2-22

变量 查看系统变量 修改 用户变量 赋值 定义处理程序 流程控制 IF CASE LOOP语句 WHILE循环 REPEAT leave ITERATE 游标 使用步骤 全局系统变量持久化 触发器 查看触发器 优点 MYSQL8的新特性 移除的旧特性 窗口函数 公用表表达式 JDBC 存储引擎 MYISAM存储引擎 InnoDB存储引擎…

跟着pink老师前端入门教程(JavaScript)-day05

六、语句 (一)表达式和语句 1、表达式 表达式是可以被求值的代码,JavaScript 引擎会将其计算出一个结果。 2、语句 语句是一段可以执行的代码。 比如: prompt() 可以弹出一个输入框,还有 if语句 for 循环语句等…

npm run serve启动报错npm ERR! Missing script: “serve“

启动项目的时候用npm run serve发现报了以下的错误 解决方法: 1.一般情况下,这个问题是因为package.json文件里面确实没有 这里没有可能因为你的脚手架版本比较低,如果不想换,可以用 这里面有的 npm run dev去启动也是可以的 n…

ubuntu20.04安装实时内核补丁PREEMPT_RT

参考: Ubuntu 18.04安装 RT-PREEMPT 实时内核及补丁【过程记录】_ubuntu18.04 preempt rt linux 5.6.19-CSDN博客 https://github.com/UniversalRobots/Universal_Robots_ROS_Driver/blob/master/ur_robot_driver/doc/real_time.md当前内核:5.15.0-94-ge…

前端基础自学整理|DOM树

DOM,文档对象模型(Document Object Model),简单的说,DOM是一种理念,一种思想,一个与系统平台和编程语言无关的接口,一种方法, 使 Web开发人员可以访问HTML元素!不是具体方…

LLM之RAG实战(二十七)| 如何评估RAG系统

有没有想过今天的一些应用程序是如何看起来几乎神奇地智能的?这种魔力很大一部分来自于一种叫做RAG和LLM的东西。把RAG(Retrieval Augmented Generation)想象成人工智能世界里聪明的书呆子,它会挖掘大量信息,准确地找到…

GitLab私有Git

GitLab私有Git 1 GitLab简介 GitLab是整个DevOps生命周期的第一个单一应用程序。只有GitLab才能启用Concurrent DevOps,从组件链的约束中解锁组织。GitLab提供无与伦比的可见性,更高的效率和全面的治理。这使得软件生命周期加快了200%&…

Flutter插件开发指南01: 通道Channel的编写与实现

Flutter插件开发指南01: 通道Channel的编写与实现 视频 https://www.bilibili.com/video/BV1ih4y1E7E3/ 前言 本文将会通过一个加法计算,来实现 Channel 的双向通讯,让大家有个一个体会。 Flutter插件 Flutter插件是Flutter应用程序与原生平台之间的桥…

转转交易猫自带客服多模板全开源完整定制版源码

源码获取方式 搜一搜:万能工具箱合集 点击资源库直接进去获取源码即可 如果没看到就是待更新,会陆续更新上 或 源码软件库 软件介绍 商品发布;请在后台商品添加成功后,再点击该商品管理,可重新编辑当前商品的所有信息…