背景
“设计模式没什么用,真正设计框架的又有几个人呢”。在我个人看来,话是没错,不学习设计模式对日常开发工作几乎产生不了多大影响。那我为什么要写设计模式呢?是因为我自己经历了几件事情,让我越来越觉得设计模式重要了,而且我认为一定要学习,那么接下来像品茶一样品一下设计模式吧!
遇到了什么事情
Scene1:上半年做了个复杂的会员卡的知识付费需求,在做会员权益的课程列表时,使用了列表卡片的公共业务组件,要扩展时发现核心逻辑代码全都簇拥在一个代码块,想要加一个新的类型需要每一行代码都读一下甚至打断点调试一下,还有可能会漏掉某些场景,函数职责太大,修改容易影响原有逻辑。
Scene2:前段时间代码评审时发现,无论是历史代码还是版本代码,总是发现有一大堆if...else和switch...case逻辑,中间一大片代码块光看代码缩进都眼晕,如果新增一条新的又要写一条判断分支,可读性和扩展性都比较差。
Scene3:有个版本需求是做一个AI的视频编辑器,在开发设计阶段,大佬提出启一个新的工程,以包的方式引入到各个使用的业务中,虽然前期要做一些基建,但是做到了整体将编辑器与其他业务模块分离,从多场景,跨产品的使用结果看来,若不是大佬和坚持与远见,我们可能要为了某一个使用场景花大量时间调试和扩写,无疑体现了大佬的功力和境界。
设计理念
根据上述经历,我发现大多数人缺少的并不是开发能力,而是耐打的设计能力,大佬之所以能够未雨绸缪,一方面是因为大佬项目经验丰富,另一方面是因为大佬的架构思维和设计理念超前。
学习意义
- 解决复杂问题,对于简单问题不能滥用,否则更复杂。
- 提升整体代码质量,让“屎山”不堆积或者少堆积。
- 为各个框架、库的源码阅读提供思想上的支持。
- 面试时思维更开阔,回答更有深度。
设计目标
我们学习设计模式的终极目标是为了尽可能让程序简洁而优雅,需要设计出高复用和易扩展的应用程序。
- 高复用:高复用指的是我们的应用程序,如类、函数、组件等,能够简单复用,秉持着一个原则,能复用的就不要自己再写了,节省时间和成本。
- 易扩展:易扩展指的是我们的应用程序在设计时,要做到未雨绸缪,根据设计原则并且留有一些扩展坑位,在扩展时不影响现有逻辑,避免写一个功能影响多个功能。
基本原则
- 封装:顾名思义,将类、方法封装,把功能与功能之间隔离,相互不受影响。
- 面向接口:接口是类成员和行为的约束,所以依赖于抽象而不依赖于具体的类,这样可以保证易扩展性。
- 组合优于继承:继承固然重要,许多场景使用继承会臃肿累赘,如写一个不重要但必须有的功能有可能继承了一个包含许多其他功能的基类,所以在这种场景下,组合的思想就极其重要了,是灵活的接口和抽象类发挥作用的地方。
solid原则
solid原则是设计模式总结出来的最佳实践,有五个原则分别是单一职责、开闭原则、里氏替换、接口隔离和依赖倒置,取每一个英文单词的首字母,称为solid。
单一职责(SRP)
一个类、函数,只负责一件事情。这样做为了避免功能耦合,修改一处功能波及其他功能。
SRP虽然是solid原则中最简单最基本的,但也是最有技术含量的,技术含量来源于分离代码的设计。有的场景需要分离,如列表分页功能的页数变化逻辑和数据业务逻辑;有些场景不需要分离,如一些工具方法,可以传字符串也可以传字符串数组,没必要分离的原因也有一定的设计理念,降低使用者的心智负担。
开放封闭原则(OCP)
类、函数、模块等,应对扩展开放,对修改封闭。这个原则更多是为了让我们有一个扩展性思想,鼓励我们扩展而不是修改原有逻辑,避免修改旧代码引入新错误。
里氏替换原则(LCP)
子类的实例对象可以替换父类的实例对象,这个原则是强调了继承的思想,鼓励子类完全实现父类的行为,这样才能够无缝替换。
接口隔离原则(ISP)
接口而应该细颗粒度以及具体化,不建议臃肿的接口,有效减少了用户在实现某个接口的行为时依赖许多用不到的功能。
依赖倒置原则(DIP)
面向接口编程,鼓励使用接口和抽象类,从抽象到具体,而不是直接就是具体的实现。在很多设计模式中会引入一个中间人的角色,从而实现业务逻辑和数据访问的解耦。
最小知识原则
也叫迪米特法则,在类、函数、模块之间通信时,实体应仅与最近的实体通信而非更远的,且两个实体之间尽可能少的了解彼此。
前端必会的设计模式
常见的经典设计模式有23种,由于JavaScript并不是标准的以类为基础的面向对象编程语言,但随着ECMAScript和TypeScript的发展,JavaScript也支持了以类为基础的写法。如ES6中的class、ES2020中的私有成员。
前端凭借着个人理解,“前端必会的设计模式”系列中分享一些对于前端而言重点要掌握的设计模式,代码均使用TypeScript实现。
创建型
创建型设计模式提供一种创建对象的方式,使代码灵活可扩展。
单例模式
单例模式,保证一个类只能有一个实例对象,唯一的访问点。核心思想是通过限制实例数量,保证全局唯一,便于管理和维护。
工厂模式
工厂模式,通过工厂提供的统一接口来得到实例对象,具体创建逻辑在工厂,客户端只需要调用工厂方法即可。大致有三种变体,简单工厂、工厂方法、抽象工厂。工厂方法是简单工厂的优化,抽象工厂更适合多维度创建。
结构型
结构型设计模式更注重体系结构,将类和对象组成更大的结构,同时保持灵活和可扩展。
装饰器模式
装饰器模式,是一种组合思想,可以将对象放入特殊对象中,从而绑定新行为。用这种方式达到一种面向切面的状态,利于模块之间解耦。
适配器模式
适配器模式,让不兼容的对象可以转换成兼容的对象,利用了中间人角色,扮演适配器,将不兼容的对象通过适配器的包装后,达成兼容目标。
代理模式
代理模式,让外界与中间人通信,中间人扮演代理,内部对外界信息进行识别,有效信息与系统内部交互,无效信息过滤掉。
行为型
行为型设计模式主要研究对象与对象之间通信,如何安全高效做职责分配。
观察者模式
观察者模式,定义一种订阅的机制,当事件执行后,会发送给所有的观察者,观察者们可以自行处理事件消息。
发布订阅模式
发布订阅模式,属于对观察者模式的优化和升级,引入了一个中间人转发消息,从而使发布者和订阅者各司其职。
模板模式
模板模式,在父类中定义一个流程,子类继承重写来保证顺序,不修改原有结构。
策略模式
策略模式,封装一些逻辑功能,在调用时可以根据参数值进行选择替换。
总结
设计模式是一把双刃剑,可以用来提升代码质量,但也可以提高代码复杂度,需要善加利用。具体问题具体分析,不能滥用也不能只用那么一两种,他们各有千秋,无法确定哪个设计模式最好,适合项目环境的才是最好的。前端必须掌握的设计模式专栏到此已完结,如果对您有帮助可以点个赞点个关注哦!