篇幅较长,建议收藏阅读;
底部有公众号、源码等,可先根据目录到最下面扫码关注或 star 源码;
从开始学设计模式到现在,算是学了两遍,但是这玩意怎么说呢,不是说学了就能会的那种,是需要经过一段时间的沉淀和积累的,有条件的话最好能直接上生产,但是需要注意项目的风险性接受程度;
一般来说,从项目的迭代之初,这个时间点是最考验我们的设计、架构思维的,如果在这个阶段你能有机会去参与到 代码规范审查、代码结构设计,那请收起你那不要钱的面子,这个时候就是你展现自己装逼实力的时候,往上奥力给,其实这个阶段如果你已经具备丰富的业务能力+你拥有较为丰富的架构思维,你可以自己尝试通过各种设计组合去完成项目的初始化!但是这里有个问题,你要因为自己设计出问题而买单;
如果你是个偶像实习生或者是个职场小鲜肉,那你是幸运的,你可以有个pm来帮你承担部分风险,最起码在review的时候能看出一点小问题,相当于给你写的代码免费提供了保险,小伙子,买保险不咯,那种可以让你随便写代码的保险哦!这个时候是可以让自己在代码设计方面有个很大的提升,多尝试才能成功吗,对吧,而且这部分业务可能也不是核心,所以大胆尝试吧!
这是张设计模式的关系图,大家应该也能看出来,但是没啥用吧?哈哈哈,可能吧,凑个数,这样好看点,其实我们从图中我们可以得出一个结论:设计模式是组合在一起使用的,那样可以将整个业务都结合起来,对于有很强代码阅读能力的人来讲,看完代码,业务也就了解了(当然我是菜鸟,哈哈哈,之前看我pm的代码,还是挺容易渗透到业务层面的)
这张图看看就得了,没必要发大时间总是想着去关联他们,到后面用的多了,自然而然就熟悉了,就会有一种从化神到飞升的感觉,一下子就悟了!
规约,是的可以这么理解,为了让代码统一,才有了这些原则,能够让代码的质量更高,不管谁写的代码,大家都能读懂,写代码随心所欲并不是个好事,大家都要守规矩的,才不会浪费别人的时间,而设计原则就是起到这个作用的!
字面上来看,就是每个对象都应该有自己的职责,别人不会影响自己,自己也不能插手去管别人;
比较官方的说法:
单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,是面向对象的五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因,该原则由罗伯特·C.马丁(Robert C.Martin)在《敏捷软件开发:原则、模式与实践》一书中提出。
举个栗子 :一个装油的油壶,我们就不应该在赋予他装饮用水的功能,那这样每次我们用的时候还需要最前置的清洗,这样功能混在一起,需要花费很多时间去维护,这样得不偿失!
在面向对象编程领域中,开闭原则 规定“软件中的 对象( 类__,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的 ”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。引用自百度百科;
开闭原则(Open-Close Principle,OCP):就是对类的高度封装,不要去破坏原有的代码,我们可以基于他去进行扩展,或者通过方法去暴露他的属性;
举个很简单的例子 :我们设计一个类,他具备了A功能给C这个类使用,突然B这个类觉得A功能不适合自己,然后偷偷的(很多业务不知道的情况下,我们不能随便更改之前的功能)将A功能改成适合自己的A1功能,那这个时候C类就无法使用了,就要骂娘了!
啧啧,都是人,为啥差距这么大呢,都已人家名字进行命名:
里氏替换原则(Liskov Substitution Principle,LSP)是由麻省理工学院计算机科学系教授芭芭拉·利斯科夫(Barbara Liskov)于 1987 年在“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstractionand Hierarchy)里提出的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立。
这个原则大部分是限制在继承之上的,大概可以分为几种情况:
子类可以实现父类的抽象方法,但是不能覆盖父类的方法
子类可以有自己独特的方法
子类的入参需要更加宽松,比如父类接收参数ArrayList,那么子类应该要能接收ArrayList及其父类
子类的返回值,因该是父类可以接收的类型,父类返回List,子类则需要返回List的本身和实现者
这个原则,可以让我们的代码健壮性加强,而且扩展性也会有一定的提升,以及新需求介入的时候,代码侵入性引起的风险也会降低
1987年秋天,迪米特法则由美国Northeastern University的Ian Holland提出,被UML的创始者之一Booch等人普及。后来,因为经典著作The PragmaticProgrammer而广为人知。迪米特法则(Law of Demeter,LoD)又称为最少知道原则(Least KnowledgePrinciple,LKP),是指一个对象类对于其他对象类来说,知道得越少越好。也就是说,两个类之间不要有过多的耦合关系,保持最少关联性。
迪米特法则的暗语:不要和陌生人讲话哦,跟自己朋友聊就行了!
举个栗子 :部门的事业部经理想要知道整个部门的收入,他肯定是去找各个产品部的经理询问数据然后拿出24K黄金的计算器开始求和,这里事业部经理肯定是不知道他们是怎么统计出来的,可能是底层打工人程序员从数仓里面拿出来的,也可能是dba从dts里面统计出来的,但是事业部总经理是不知道的,也不能知道,去自个写代码统计,然后各种数据不对!!其实也有但各司其事的意思;
《代码整洁之道》的作者Robert C.Martin于2002年给“接口隔离原则”的定义是:客户端不应该被迫依赖于它不使用的方法(Clients should not be forced todepend on methods they do not use)。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class toanother one should depend on the smallest possible interface)。
接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含调用方需要用到的方法。
举个栗子:我们设计一个接口,叫容器,我在其里面定义了,装水、装油、装大米三个方法;然后我用我现在有个麻袋,去实现这个容器,这就有问题了,我只需要有装大米的功能,现在我还要去实现装水、装油,我根本实现不了,这个时候在代码里面只能不做任何处理,这样导致无用代码大量增加;而如果容器只提供装东西的功能,我在写一个接口:大米容器,里面只有装大米的功能,这个时候只需要实现一个方法就行,虽然这样类会多了不少,但是对于后续扩展就会减少很多无用代码段;
依赖倒置原则(Dependence Inversion Principle,DIP)是指在设计代码架构时,高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。在Spring生态中,我们在实际的c层或者其他的si(ServiceImpl)层级ddd下的manager层我们都应该只注入接口,而不是去注入ServiceImpl 。
简单来讲:最好不要两个实现类互相调用,而是使用其接口进行调用
举个栗子 :比如A接口有A1、A2两个方法,然后B类和C类分别实现一个方法,B-A1,C-A2,然后有个D类现在要用A中的方法,他可以选择分别引入 B类和C类,然后去调用,这样就多了很多无效的代码,引入A就可以了,就像我们平时的MVC模式下,我们一般在controller一般都是引入Service层的接口,不会去引入实现类!
合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。
这个原则用的很少,继承的扩展性很强,所以里氏替换我是觉得有点冲突,不多讲,聚合关系是很好用的一种思想,这个可以参考借鉴的,这个原则我有点没吃透,大家可以补充!
使用花里胡哨的方式创建对象,其实就是将创建对象的过程给隐藏起来了,然后根据某些规则获取这些对象,使其可以更加方便的使用,无需去new,这样更灵活!
工厂模式也称简单工厂模式,是创建型设计模式的一种,这种设计模式提供了按需创建对象的最佳方式。同时,这种创建方式不会对外暴露创建细节,并且会通过一个统一的接口创建所需对象;
其主要作用就是减少if else,然后对对象的创建进行统一的管理;
但是他也会存在一个问题,如果需要创建的类型过多,也会导致工厂类里面的if else大量增加,去维护对应的type也是需要花费一定时间的,而且功能类的数量也会大量增加;
举个例子 :创建一个接口,容器(Column: 抽象产品类),然后有存液体的功能;然后有两个实现类(WaterColumn:具体产品类)、(TeaColumn:具体产品类),再通过工厂类(ColumnFactory:工厂类,这里)去根据不同类型,选择行创建不同功能类,想喝茶就去创建 Tea;大概就是如此!
看看图:
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
这里可以理解为,一个很大的工厂,比如电脑厂(ComputerFactory),他可能旗下还有 屏幕厂(MonitorFactory)、硬盘厂(HardDiskFactory)等,这里电脑厂就是抽象工厂,然后子厂再去生产指定的东西(功能类);
其实这种模式在平时开发用的不算多,大多数情况下通过简单工厂就能完成,而且这种模式下,会导致子类工厂需要去实现自己没有的功能,这样不算符合接口隔离原则,但是我们可以稍作修改,看下图,我们可以在新增一个中间实现(CommonFactory),他不做任何处理,就替代功能类去实现自己没能力完成的功能,他自己的作用就是减少冗余的无用的代码;
看看图:
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一(主要减少内存占用)。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
可以总结出一条经验,单例模式主要解决的是一个全局使用的类,被频繁地创建与销毁,从而提升代码的整体性能;
这个模式没什么好说的,是比较简单的,主要目的就是为了减少对象频繁创建而浪费内存
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的,相当于聚合其他的类,完成最终可供选择的产品;这里可以抽象为表示与构建过程进行分离,客户端只需关注表示出来的东西;
建造者模式的经典案例就是lombok的@Builder注解,提供建造者模式去构建自己最后想要的对象,这里没有用的小伙伴可以去试试;
这里举个栗子 :有个电脑店(ComputerBuilder),他有很多材料可以使用,最后需要给出几套方案:有高配的、中配的、低配的;顾客想买的话就直接提走就行了,不需要自己组装电脑(当然向我们程序员肯定是要自己体验组装的乐趣了),这大概就是建造者模式的概念;
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能,这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时(存在一个对象创建过程很复杂的情况,这样就需要选择去复用对象了),则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建(一般都是初始化的数据,另外一种方式则是放到redis这一类缓存数据库中)。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
原型可以涉及到java中的深拷贝的创建方式,使用的就是.clone方法,这个模式其实在代码中用的很多,比如将重复使用的对象放到map中,或者放在ThreadLocal中都是这种模式的思想
结构结构,指的就是对象有独特的结构,就是通过对象的各种组合来达到产品需求;它主要包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式这个七个模式。
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
适配器模式的主要作用是把原本不兼容的接口通过适配修改做到统一,方便调用方使用.
生活中有很多这种案例,最常见的就是电源适配器,有电脑的、手机的都是将家庭用电转换成电子产品对应的充电需要的电压数;
这里举个小栗子 :有一个远古的MP3播放器,正在努力的播放MP3文件,突然新生想法,我要播放MP4文件,甚至avi文件;然后开始动手做了个适配器(MediaAdapter),然后给他赋予了播放前面两种文件的能力,然后在把适配器插入到之前老的MP3播放器中,然后就可以美滋滋的看着avi文件的电影了;
桥接(Bridge Pattern)是用于把抽象化与实现化解耦,使得二者可以独立变化。然后两者通过一个抽象类来进行桥接;这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。可以理解为 A功能类、B功能类,然后一个抽象类C作为桥接者,将A和B两者连接起来,这样A、B两者可以修改自己的类的行为,然后C不去桥接两者,这样可以灵活的配置;如果出现很多组合,然而每一个类都实现不同的服务,可能会出现笛卡儿积,这个时候用桥接就特别爽了!
看看这个例子: 我是个吃货,假设有家店,他有很多菜品(Cuisine),这些菜品呢,就是那个桥接者,然后呢,有面食(Pasta)、米粉(RiceFlour);然后厨师会两种做法水煮(Boiled)、爆炒(StirFry);那么笛卡尔积就来,2 * 2 = 4,就存在四个菜品:水煮面、爆炒面、水煮粉、爆炒粉;突然厨子A离职了,然后厨子B来了,假设他是个菜鸟,只会水煮,那么菜品只能减少了(Cuisine);但是只要厨子A又回来了,或者将爆炒改良了成魔鬼辣椒爆炒,但是对于菜品来说,只需要将面食丢给厨子就行了,我菜品上又可以加两道菜;这就是桥接的好处就是应对这种会存在组合关系,会出现笛卡尔积的场景;
组合模式(Composite Pattern):有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。 — from 百度百科
组合模式,光看概念可能会很模糊,那就举个栗子:什么天气穿什么衣服;首先我们有很多衣服 长袖、短袖、五分裤、九分裤(这里可以通过桥接、建造者去组合);然后有几个条件:温度有大于、小于30°,太阳分 有、无,这里可能还有风量大小之类的,就先不说了;然后会出现很多组合:
温度>30°,有太阳 ==》 穿短袖、五分裤
温度>30°,无太阳 ==》 穿短袖、九分裤
温度<30°,有太阳 ==》 穿长袖、五分裤
温度<30°,无太阳 ==》 穿长袖、九分裤
看上面我们可以这么做,将每个条件抽离出来(温度、太阳)作为一个个节点,然后再将 >、<、=、有、无这一类作为对比规则保存起来,用一个switch+枚举保存;然后开始组合 温度节点 → 规则与界限(>, 30°)→ 太阳节点 → 规则与界限(=, 有太阳)→ 果实节点(这里就是具体返回 == 穿短袖、五分裤);这样就可以组合出很多场景来了,这样虽然看起来没有if else来的快,可是之后需要加 温度界限为 10°、 20°等情况的时候,有需要加很多if else,这样维护起来就相当麻烦,而组合模式,这需要加上一个条件节点、一个规则节点和果实节点,也不会对之前的代码有很大的侵入性;
当然这是举个栗子,组合模式也可以用来做决策树、根据用户性别和年龄推荐不同类目的产品;
看看图:
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式类似于俄罗斯套娃;
这种模式并不少见,很多熟悉的场景都用到了装饰器模式,例如是否熟悉 new BufferedReader(newFileReader(""));这段代码?大家在学习Java开发的字节流、字符流和文件流的内容时都见到过,一层嵌套一层,字节流转字符流等。这就是使用装饰器模式的一种体现;
再来举个栗子 ,让大家更明白:假设我有辆装逼的机车(Locomotive),虽然很装逼,但是总感觉有点不足,那个轰鸣声虽然大;但是不够,于是我想改造,装个音箱来放dj(装饰物:音箱),这个时候我还需要一个放音箱的架子(CarDecorator);然后我把音箱放上去就可以快乐的听dj了,快乐的装逼了!!
看图:
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。他类似于在多个系统中做一个中间者的模式,封装一个复杂的逻辑给调用方使用;
举个小例子:相当于有两个系统A、B;这两个系统都需要使用支付,但是支付系统C想调用很复杂,于是乎又来了第三个系统D(支付sdk);此时A、B只需要引入C系统的sdk即可选择支付方式;之后想增加支付方式,继续往系统D中添加即可;
看图:
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能;较大的对象通常比较耗费内存,需要查询大量的接口或使用数据库资源,因此有必要统一抽离出来作为共享对象使用。像数据库连接池就是很好的一种实现;
这个模式就是将大对象复用,把其放到缓存中;这里是完全参考菜鸟上面的享元模式,就是将圆的颜色进行缓存,然后其他属性随机变化;
直接看图:
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
代理模式并不少见,想在java中就有jdk动态代理,spring的cglib等都是使用了代理模式;
这里举个栗子:有一张照片(Image ),需要从磁盘里面取出来,如果我们每次都去磁盘里面区,就会造成大量的IO(之前的设计是如此,不好更改),会影响系统的性能,而这个时候就可以创建一个代理类,去代替这个对象对外提供访问入口,但是不会改变其功能,依旧可以展示图片,只是不需要去磁盘中取;
看图:
每个对象都在尽自己的职责在做事,可以理解为这一类设计模式是通过自己的行为去达成目标,而不需外部继续实现
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。一般来说,这种模式下,上一个处理不了或者已经处理了一次,便会把自己的对象引用丢给下一个处理节点继续操作,他们都继承了同一个抽象,而该抽象中包含了下一个节点的引用;相当于是一个拦截链,处理不了就回去请求下一个处理者的帮助,也有可能是每个拦截节点都会对一个对象添加一个新的功能点;
这里举个例子: 有三种日志级别,ConsoleLogger、DebugLogger、ErrorLogger,假设我们初始化的时候,使其对应的级别为1,2,3;那么这个时候我们针对不同级别的日志输出不同的内容,假设我们这里的优先级是,3可以打印 123, 2可以打印 12,1只能打印1;当我们是3的时候,此时我们的规则就是 3≥1打印 console级别,3≥2打印debug级别,3≥3打印error级别;如果不用责任链,我们可能就绪要写很多种if else的组合;使用责任链后:(AbstractLogger),然后几个级别继承该抽象,然后里面存有 next节点,处理完该节点够,继续处理next的节点;
看图吧:
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。命令模式是把逻辑实现与操作请求分离,降低耦合,方便扩展。命令模式是行为模式中的一种,以数据驱动的方式将命令对象用构造函数的方式传递给调用者。调用者再提供相应的实现,为命令执行提供操作方法
这种模式用的比较少,但是有挺常见,比如我们最爱用的 CV大法;
来一个例子: 模拟一个点菜系统,当顾客下单点菜的时候,他肯定是找服务员(XiaoEr)去下单,然后服务员再去找厨师做菜,在这里可以理解,顾客的 江西厨师做的江西菜就是一个命令,然后告诉小二,然后小二会将这条命令传递到厨师就行了,顾客无需跟厨师进行直接交流;反过来想,如果厨师已经做好了菜品,小二只需要根据 顾客的命令 拿到对应的菜品即可;
看图:
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
这个模式平时用的不多;主要是用来设置多个表达式模板,达到代码复用的目的,比如我要校验一个sql语句是否是select,然后在做出相应的处理;不多说
看图:
迭代器模式(Iterator Pattern)是 Java编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式属于行为型模式。可以理解为是for的高级封装,是同一个功能的不同实现;迭代器模式的优点是能够以相同的方式遍历不同的数据结构元素,这些数据结构包括:数组、链表和树等。而用户在使用遍历时,并不需要关心每一种数据结构的遍历处理逻辑,做到让使用变得统一易用
迭代器模式其实没什么可讲的,在java中就很常见,有很多实现,这几就简单参考菜鸟上面的案例,遍历名字打印在控制台;这个也没必要说很多;
看图:
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。其实也可以理解为将复杂的类调用关系,全部堆到中介者的身上,以此类维护这些类的关系;
又要举个例子来讲讲:中介者模式当然要扯上租房中介了,假设没有中介的情况下,A房东有房子,B租客直接联系了A,然后A去接B,到了房子,看房子,然后租下,然后B的朋友C租客也想租房子,B联系房东,房东到B那取得C的联系方式,加了微信,问了C位置,又去接他来看房,然后房东累死了,C不租了;房东一怒以下,花点钱请中介,然你们租客租房多花点钱;于是乎中介来了;现在有个D租房,直接找中介,中介接他来看房,房东不管了,中介负责告诉房东结果,最后付钱签合同在找房东即可;这里中介者就省去了人找人(类与类的耦合),又要房东去接,又要介绍房子等繁琐的事情;吐槽:房租好贵啊!
看图:
备忘录模式(Memento Pattern)保存一个对象的某个状态的快照(会存在多个快照),以便在适当的时候恢复对象。备忘录模式属于行为型模式。在功能实现上,是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为,从而实现备忘录模式;相当于记录了多个版本,随时有恢复的手段;类似于k8s的弹性伸缩,或者说虚拟机的快照版本;提供后悔的功能;
备忘录模式,其实就是将历史版本存放起来,便于之后去取出来进行回滚,其实数据库里面取最后一次操作日志是同一个道理,想拉链表也是如此设计,记录很多版本;
举个例子 :我现在有个操作,每个操作都对应一个版本;现在我希望我有个功能可以去实现我能回到上一个版本或者任意版本;有个操作(Originator)还有版本号,这个每次操作都会记录到备忘录;然后就有一个备忘录记录操作内容和版本号(Memento),并将历史版本存起来,这里是放到List中;之后想回滚直接取出来就行了;
看图:
观察者模式(Observer Pattern)一般用于一对多的情况下。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。简单来讲,观察者模式是指当一个行为发生时,一个用户传递信息,另一个用户接收信息并做出相应的处理,行为和接收者之间没有直接的耦合关联,这个也可以参考发布订阅模式,在Mq中是非常常见的一种模式;
把观察者模式说成发布订阅模式也很类似,意味着所有订阅发布者消息的,当消息改变,或者有新的传递的时候,订阅者(观察者)是能实时接收到的,而自己无需做任何操作;
来个小例子: 在有订单的系统中,都会有存在发消息提示管理员有人下单了,有钉钉、微信模板、短信等推送方式;这里我们模拟下单操作,首先会有个事件处理器(EventHandler)里面只有处理消息的功能,这里一般会把订单信息传递过去;然后有两个实现(MQEventHandler,MessageEventHandler)里面是MQ去存库和发短信的操作;然后就是有个监听管理器(ListenerManager),这里会初始化所有需要通知的处理器(订阅者);然后就是发布者(OrderService)这个设计为抽象类,这里在订单下单成功,然后更具业务需求选择指定的发送给哪个订阅者,当然我们也可以设计成默认发所有;
看图吧:
在状态模式(State Pattern)中,上下文类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象(将状态类赋值给上下文)。其实这个很常见,比如登录和不登录场景下,就是不同的状态对应不同的页面;每种状态只关心自己的行为,而状态和行为的表示,则交由context上下文去表示,这样看上去是 state这个对象一直在改变;
这里有个小场景:假设我们下单现在之后两个操作,开始下单准备(StartState)、下单完成状态(StopState)、上下文(Context)用来管理表示这些状态;当进行每一个操作的时候,将其赋值给上下文,用他来对外暴露;这个时候的上下文是需要根据 两种状态来进行改变的;
看图:
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改且不影响调用者的代码逻辑。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象(不同行为或不同算法、规则模板等)和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
这里举个敲简单的例子: 我有两套算法,很高大上的算法,加法(AddStrategy)、乘法(MutilStrategy),这些都继承策略模板(Strategy),该策略就是计算;然后就是有个跟算法实现无关的类,也即是上下文(Context),只需要传递指定的算法就行,然后不需要修改任何代码,当修改算法时,上下文对应的反馈也会随之修改;
看图吧:
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板(就是方法ABC,其子类都有这些流程)。它的子类可以按需要重写方法实现,如果有需要实现的,可以使用公共的继承类去减少冗余的实现代码,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。模板模式的核心设计思路是通过在抽象类中定义抽象方法的执行顺序,并将抽象方法设定为只有子类实现,但不设计独立访问的方法。
这种模式还是挺多地方,或者场景都用到了,比如工厂里面的活,生产一瓶牛奶,取出盒子 → 装入牛奶 → 塑封 → 粘上习惯 → 打包装箱;这些都可以抽象成模板,但是装入什么牛奶,用什么习惯就有不同牛奶品牌去决定;
举个在简单点的例子: 吃货的我,来说的跟做菜相关的,我们做菜可以分为 清理菜(cleaning) → 做菜(doCooking) → 装盘(finish),将这些步骤全部封装起来做一个模板,然后具体子类去实现就行,我们这里还可以提取出公共实现,然后子类实现自己的步骤即可,比如白菜(Dabaicai),比较普通,需要全部做完上述模板操作,而我有个精品的五花肉(WuHuaRou),我直接做就可以了,不需要处理,两步就搞定了;这样防止过多冗余代码,我们就可以搞个公共类去空实现;
看图:
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类去得到自己想要的结果,简单来说就是访问者想通过指定的算法得到自己想要的结果,这个指定的算法是由被访问者提供的。
举个例子吧: 存在两个访问者校长(Principal)、家长(Parent);两个被访问者老师(Teacher),用于给校长汇报及格率,学生(Student)用于给自己家长汇报班级排名;当校长或者家长突然不想看及格率胡总和排名了,对应的老师、学生就需要对自己原有的逻辑或者算法进行修改;这就是被访问者做出的行为;
看图:
其实就是简单的写了个目录一样,算是一篇快速过一遍概念的总览文章,具体的代码还是需要到具体的文章上去看看,或者自己根据代码去自己手动敲一下(强烈推荐),最好是敲一遍我写的,然后理解之后再去自己像一个场景,然后去实现它,这样之后实际生产中就会得心应手!!加油吧!!
重点: 之后详细文章可能会放在公众号,或者在csdn中继续发;请持续关注:
公众号: 扫码关注,爱搞技术的吴同学 ,公众号上会经常写实用性的文章,谢谢关注!!回复:“加好友”,可获取我的微信二维码,欢迎加好友,一起学习!!!
大量源码: 欢迎star,可能会分享微服务实战,分页插件等;gitee-yysimple
感谢大家阅读!!!
参考:https://www.runoob.com/design-pattern/design-pattern-tutorial.html
参考:https://weread.qq.com/web/reader/bcf32900724708cbbcf08c1k8f132430178f14e45fce0f7(小傅哥)