23种设计模式的总结与思考

image.png
image.png

创建型模式,共五种:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:
策略模式、模板方法模式、观者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

https://www.runoob.com/design-pattern/design-pattern-tutorial.html
Java的完整总结,查询很方便

https://github.com/JsonChao/Awesome-Android-Notebook/blob/master/notes/Android%E5%BC%80%E5%8F%91%E8%80%85%E5%BF%85%E9%A1%BB%E6%8E%8C%E6%8F%A1%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md
Java中的常用总结
https://www.cnblogs.com/pony1223/p/7608955.html
很详细,有图
https://zhuanlan.zhihu.com/p/136592911
几种常用的介绍,demo很生动

上面是找的一些资料。

前言

设计模式对软件开发是非常重要的,理解设计模式需要一定的代码经验,如果经验不足,面对几十个设计模式,大部分没写过也没见过,肯定难以掌握。或许为了写而写,在项目里强行塞进去几个,然后从脑海搜刮出来交给面试官。

其实设计模式是软件开发中的自然行为,有可能写代码时不知不觉使用了设计模式,设计模式是从代码中总结出来的,先有代码,后有设计模式。你也完全可以从代码中总结出一种新的设计模式。

有了一些年开发经验,再回头看设计模式,就能发现它们都是很简单自然的(如果一个模式很复杂难以理解,为什么要去用它?),在设计架构或者优化代码时,可以自然地考虑某个地方可以使用哪种设计模式。还有一点,就是不同的开发:前端、后端、SDK等,对设计模式的接触和使用肯定是不同的。我从一个多年Android APP开发者的角度,总结一下23种设计模式。个人水平有限,有不准确处还请指出。

因为设计模式的示例代码和类图在网上都很容易找到,我就不再复制,主要以很简单的语言描述使用方法、使用场景、模式的本质等。

1 创建型

单例模式:某个类只能有一个实例,提供一个全局的访问点。

单例模式是大部分开发者接触的第一个设计模式。它一般用来做一些业务逻辑管理类,或者用来保存一些全局变量。安卓不鼓励使用全局变量(鼓励序列化传参,实际上相当繁琐,尤其是传参层级多时),用单例管理全句变量便于统一初始化或销毁。

单例模式在Java里有好几种写法,主要考虑两点:创建时机和高并发失效。在类加载时提前创建(饿汉式)比较简单,但会占用内存和影响启动速度;使用时创建(懒汉式)需要考虑高并发失效,也就是创建实例时多个线程挤进来,导致创建多个实例,标准写法是用DCL加volatile关键字。其实不同于服务端开发,安卓里很少有高并发场景。

本质就是一个静态全局对象的创建。

简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。

工厂模式是将创建类实例的方法封装到一个工具类里面。有的类创建比较麻烦、属性很多,比如创建一个View需要设置很多属性:大小、颜色、字体、间距等等,这些属性又是固定的,那么封装到工厂类里面,不需要每次都设置相同属性。

本质就是一个简单的方法封装。

抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。

抽象工厂是在工厂基础上做一点扩展,将工厂的方法抽象成接口,然后可以有多个工厂类实现这个接口,那么就可以创建不同的工厂类,通过相同接口调用不同工厂的创建方法。

说实话,接口和多态是OOP的基本特性,为什么要加到工厂上面来强调一个抽象工厂,我不是很理解。安卓里也暂时没想到使用的地方。

本质就是工厂加上接口和多态。

建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

建造者模式是当一个类属性很多、创建过程比较复杂时,可以先创建一个Builder类,在Builder里配置好默认属性,提供每个属性的单独set方法,最后用配置好的属性创建一个实例出来。

在安卓里使用比较多,典型的就是创建弹窗,用Builder设置标题、文本、按钮、点击事件等。开发者自己封装来说,可能SDK里面会用到多一点,一般创建对象也不是很必要使用。

它跟工厂有啥区别呢?它的使用场景是属性变化比较大,有的属性需要设置有的不需要,那么用Builder管理:“不管你怎么配置属性,我最后都给你创建一个实例出来”。在没有它之前,我们肯定要写多个构造方法传不同参数,方法里面也会有重复代码。

本质就是实例创建的封装。

原型模式:通过复制现有的实例来创建新的实例。

原型模式是当一个类创建成本比较高时(比如需要查数据库),复制现有对象来产生新对象。Java里需要利用Cloneable机制。Cloneable默认是浅拷贝的,也就是只拷贝引用,深拷贝需要自己在Clone()方法里面去实现。

很少看到有直接使用的。有一些场景是利用现有对象创建新对象,但是没有用clone()方法,不知道这算不算使用了原型模式。

本质是利用编程语言的克隆机制来创建新对象。

2 结构型

享元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

享元模式是将一些需要经常使用的对象创建并缓存起来。这是一种很简单又很常用的设计模式。我认为它比单例更普遍使用,因为Java里的常用数字如0,1,2...就是虚拟机提前创建的对象。安卓里面的列表项会在展示前提前创建,并且会缓存起来,以保证滑动流畅(缓存的重要性不言而喻,就是用空间换时间)。

本质就是缓存一些对象。

外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。

外观模式是一个模块对外提供几个调用方法,隐藏模块内部实现细节。这是很自然的封装。现实中的很多东西,比如电视机:内部电路很复杂,外面却只有几个按钮,就是相同的思想。

使用非常普遍,可以说只要有模块和封装,就会用到外观模式。

本质就是封装。

适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。

适配器模式是用一个适配器类包装某一接口实现对象,并实现另一个接口。它用来做两个接口间的适配。简单地说,C类实现了I1接口,现在要支持I2接口,那么用Adapter类实现I2接口,提供给C类(调用C类方法)。这样相当于在不修改C类的条件下,让他支持了I2接口。这也是现实中很常见的,比如“电源适配器”就是相同的思想:手机(C类)实现了5V供电接口(I1),现在怎么让它支持220V输入(I2)呢,用电源适配器实现220V,提供给手机。

适配器让某个类支持了新的接口,有点像装饰模式,但它主要是针对接口转换的。

本质就是类的引用。

装饰模式:动态的给对象添加新的功能。

装饰模式是在包装类里面给原类提供新的功能,也就是新的属性和方法。它比继承更灵活一点。为啥用它不用继承呢,这跟具体类的定义有关,如果新的功能跟原有的类关系不大,那么用继承就不合适。比如有个打印机类,黑白打印机和彩色打印机都可以作为子类,但是增加了音乐播放功能后,这个“打印播放器”就不适合继承打印机了。

它跟适配器模式的区别是它不针对某个接口功能。

本质也是类的引用。

组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。

组合模式简单理解就是树状结构,各软件的菜单结构、Java集合的继承结构、安卓View的继承机构都是这种。不多说,很好理解。

App中如果有复杂菜单可以用组合模式,SDK开发中如果有一些类是树状关系也可以用。

本质就是树状结构。

代理模式:为其他对象提供一个代理以便控制这个对象的访问。

代理模式是给某个类添加访问控制,用另一个类代理原类的某个接口方法。它是迪米特原则的直接实现,也就是是依赖最小的接口。简单地说,如果需要某个接口I1的功能,C类实现了I1接口,那么用代理类Proxy引用C类,调用者只依赖代理类,不依赖C类。

调用者直接依赖接口不是更简单吗?代理类除了提供接口实现,还能做一些访问控制。

普通的代码实现是静态代理;Java反射机制提供了Proxy.newProxyInstance()方法来创建代理对象,就不需要写代理类了,叫做动态代理;还有一些框架提供了别的代理方式。一般来说静态代理使用多一点,其他的多用于较复杂的架构设计。

本质就是类的引用。

桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

桥接模式不是很好理解,这篇文章讲得很清楚。简单地说,它是用关联关系来代替继承关系,防止继承类太多。也就是把两个类桥接起来。比如图形有形状和颜色,可以把颜色作为形状的一种属性,那么圆形、矩形都有颜色属性,而不是用红色圆形、黑色圆形来继承圆形。这么一想,桥接模式就是一种很普遍的模式了,因为很多类都包含了独立变化的属性。

既然只是类的属性,为什么要强调桥接呢?我认为这涉及到代码重构,将不适合继承的代码整理为桥接,是一种优化代码的方法。当然用接口关联代替类关联就不用再强调了。

本质就是类的关联。

3 行为型

迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

迭代器模式就是集合的Iterator。

本质就是接口封装。

观察者模式:对象间的一对多的依赖关系。

观察者模式就是注册监听和发送通知。将一个类的属性变化通知一系列提前注册的观察者。这在GUI里面是很普遍的。一般在类里创建集合对象,添加观察者,事件发生时遍历集合挨个通知观察者。

Java提供了原生的Observer,虽然很少用;安卓里的广播就是系统帮你遍历广播接收者。

本质就是集合遍历。

模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。

模板方法是父类定义操作步骤也就是几个方法的调用顺序,子类实现其中的一个或几个具体方法。

在类继承里很常见:父类大多数操作都是确定的,只有其中一两个方法需要子类重写时可以认为是模板模式。

本质就是继承和多态。

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。

简单地理解它就是解析一个句子,可以是字符串、表达式或者SQL。

使用场景很有限,极少需要开发者自己去定义。

本质是字符串解析。

策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。

策略模式是将不同条件的操作封装到几个策略子类里面,避免写很多if else判断。这是一种比较常用的模式,往往用来重构if else代码。

本质也是继承和多态。

状态模式:允许一个对象在其对象内部状态改变时改变它的行为。

状态模式就是对象根据它的一个属性的变化改变自己的状态。这是一种很自然和常用的模式。比如一个按钮有选中状态,选中状态变化时给它设置不同的图片文字。

Java里面监听属性变化需要直接调用set方法,有的语言提供了属性内部的set方法,只要属性变化就能收到监听,更方便一些。

本质就是属性监听。

备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

备忘录模式就是在外部保存对象状态,便于恢复到某个状态。这是一种很常用的模式,比如游戏存档、文本编辑器的Ctrl+z、版本管理git等。不同备忘录对保存和恢复要求是不同的。

本质是保存和恢复对象。

中介者模式:用一个中介对象来封装一系列的对象交互。

中介模式就是在一个中介类里面处理另外两个类之间的交互。它可以降低类的耦合,是非常常用的。

安卓里面各种架构:MVC、MVP、MVVM层出不穷眼花缭乱,其实都是中介者模式,区别无非是View和Model的引用放到哪个类里。

它跟代理模式有点相似,但是代理模式是针对某个接口的。

本质就是类的引用。

命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

命令模式不是很直观。简单理解如果有多个操作命令,比如点击不同按钮、CMD不同指令,统一命令处理接口,根据命令类型由不同的接口实现类处理。有时还需要考虑命令的记录、撤回、重做等。

本质就是接口和多态。

访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

访问者模式这篇文章讲解比较详细。我的理解它就是对一些数据提供了不同的访问方式,它屏蔽了数据的内部细节,也不改变数据。比如安卓里用MediaStore访问相册。

它跟外观模式有点像,不过外观模式是针对模块的调用,它是针对数据的访问。而且外观模式对外只提供一个调用类,它针对数据可以有多种访问类。

本质就是封装。

责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。

责任链模式特点很明显,就是一个发送对象有多个接收者处理,这些接收者依次处理,传给下个处理者或者不传。安卓里的有序广播、触摸事件传递、网络请求的拦截器等都是责任链模式。

它跟观察者模式有点像,不过观察者模式一般是所有接收者同时处理事件,没有链式传递事件。

本质就是链式调用。

4 设计模式的本质

我总结了一下23种设计模式的本质,见下图。

其中10种是OOP三大特性封装继承多态的直接体现,剩下的则利用了一些编程特性:3种利用了缓存机制,6种利用了引用(以某种方式持有当前对象),4种利用了方法(进行具体操作)。每个设计模式都有一个非常简单的关键词,便于快速回想它的用途。

截屏2022-04-12 上午10.29.02.png

你可能感兴趣的:(23种设计模式的总结与思考)