设计模式
2024-05-09 16:20:25 0 举报
AI智能生成
前端开发不仅仅是编写代码,更在于如何高效、可维护地构建应用程序。前端设计模式模板,旨在以一种通俗易懂、直白的方式,帮助你理解和应用常见的设计模式。
作者其他创作
大纲/内容
行为型
观察者模式
适用场景
DOM事件
vue响应式
优点
1.支持简单的广播通信,自动通知所有已经订阅过的对象
2.目标对象与观察者之间的抽象耦合关系能单独扩展以及重用
3.增加了灵活性
4.观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化
缺点
什么时候不用
例子
迭代器模式
适用场景
Array.prototype.forEach
jQuery中的$.each()
ES6 Iterator
优点
访问一个聚合对象的内容而无需暴露它的内部表示。
为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作
缺点
什么时候不用
例子
策略模式
适用场景
如果一个系统里面有很多类,他们之间的区别仅在于它们的“行为”, 那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为
一个系统需要动态的在几种算法中选择一种
表单验证
优点
利用组合、委托、多态等技术和思想,可以有效的避免多重条件选择语句
提供了对开放-封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,理解,易于扩展
利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的代替方案
缺点
会在程序中增加许多策略类或者策略对象
要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy
什么时候不用
例子
模板方法模式
适用场景
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
子类中公共的行为应被提取出来并集中到一个公共父类中的避免代码重复
优点
提取了公共代码部分,易于维护
缺点
增加了系统复杂度,主要是增加了的抽象类和类间联系
什么时候不用
例子
职责链模式
适用场景
JS 中的事件冒泡
作用域链
原型链
优点
降低耦合度。它将请求的发送者和接收者解耦
简化了对象。使得对象不需要知道链的结构
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任
增加新的请求处理类很方便。
缺点
不能保证某个请求一定会被链中的节点处理,这种情况可以在链尾增加一个保底的接受者节点来处理这种即将离开链尾的请求。
使程序中多了很多节点对象,可能再一次请求的过程中,大部分的节点并没有起到实质性的作用。他们的作用仅仅是让请求传递下去,从性能当面考虑,要避免过长的职责链到来的性能损耗
什么时候不用
例子
命令模式
适用场景
优点
对命令进行封装,使命令易于扩展和修改
命令发出者和接受者解耦,使发出者不需要知道命令的具体执行过程即可执行
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。
什么时候不用
例子
备忘录模式
适用场景
分页控件
撤销组件
优点
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
什么时候不用
例子
状态模式
适用场景
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为
一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态
优点
定义了状态与行为之间的关系,封装在一个类里,更直观清晰,增改方便
状态与状态间,行为与行为间彼此独立互不干扰
用对象代替字符串来记录当前状态,使得状态的切换更加一目了然
缺点
会在系统中定义许多状态类
逻辑分散
什么时候不用
例子
访问者模式
适用场景
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
优点
符合单一职责原则
优秀的扩展性
灵活性
缺点
具体元素对访问者公布细节,违反了迪米特原则
具体元素变更比较困难
违反了依赖倒置原则,依赖了具体类,没有依赖抽象
什么时候不用
例子
中介者模式
适用场景
系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象
想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
优点
使各对象之间耦合松散,而且可以独立地改变它们之间的交互
中介者和对象一对多的关系取代了对象之间的网状多对多的关系
如果对象之间的复杂耦合度导致维护很困难,而且耦合度随项目变化增速很快,就需要中介者重构代码
缺点
系统中会新增一个中介者对象,因 为对象之间交互的复杂性,转移成了中介者对象的复杂性,使得中介者对象经常是巨大的。中介 者对象自身往往就是一个难以维护的对象
什么时候不用
例子
解释器模式
适用场景
优点
易于改变和扩展文法。
由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法
缺点
执行效率较低,在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度慢
对于复杂的文法比较难维护
什么时候不用
例子
创建型
单例模式
适用场景
一个类只有一个实例,并提供一个访问它的全局访问点
优点
1.划分命名空间,减少全局变量
2.增强模块性,把自己的代码组织在一个全局变量名下,放在单一位置,便于维护
3.且只会实例化一次,简化了代码的调试和维护
缺点
由于单例模式提供的是一种单点访问,所以有可能导致模块间的强耦合,不利于单元测试。
什么时候不用
例子
1.定义命名空间和实现分支方法
2.登录框
3.vuex和redux的store,vueRouter
原型模式
适用场景
如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式
如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象
优点
1.你可以克隆对象, 而无需与它们所属的具体类相耦合
2.你可以克隆预生成原型, 避免反复运行初始化代码
3.你可以更方便地生成复杂对象
4.你可以用继承以外的方式来处理复杂对象的不同配置
缺点
克隆包含循环引用的复杂对象可能会非常麻烦
什么时候不用
例子
工厂模式
适用场景
1.将new操作简单封装,遇到new的时候可以考虑是否用工厂模式
2.需要依赖具体环境创建不同实例,这些实例有相同行为
优点
1.只关心创建结果
2.构造函数和创建者分离,符合"开闭原则"
3.一个调用者想创建一个对象,只要知道其名称就可以了
4.扩展性高
缺点
1.添加新产品时,需要重新编写具体产品类,增加了系统复杂度
2.考虑系统可扩展性,需要引入抽象层,在客户端代码中试用抽象层进行定义,增加了系统的抽象性和理解难度
什么时候不用
例子
1.jQuery的$()就是一个工厂函数
2.vue的异步组件
抽象工厂模式
适用场景
如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。
优点
1.同一工厂生成的产品相互匹配
2.避免客户端和具体产品代码的耦合
3.单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护
4.开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码
缺点
由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。
什么时候不用
例子
建造者模式
适用场景
1.使用生成器模式可避免 “重叠构造函数 (telescoping constructor)” 的出现
2.当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式
3.使用生成器构造组合树或其他复杂对象
优点
1.你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤
2.生成不同形式的产品时, 你可以复用相同的制造代码
3.单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来
缺点
该模式需要新增多个类, 因此代码整体复杂程度会有所增加
什么时候不用
例子
jquery的$.ajax
结构型
适配器模式
适用场景
1.整合第三方SDK
2.封装旧接口
优点
1.可以让任何两个没有关联的类一起运行
2.提高了类的复用
3.适配对象、适配库、适配数据
缺点
1.额外对象创建,非直接调用,存在一定开销
2.如果没有必要使用适配器,考虑重构
什么时候不用
例子
1.vue的computed属性
装饰器模式
适用场景
1.es7 Decorator
2.core-decorators
优点
1.装饰类和被装饰类都只关心自身的核心业务,实现了解耦
2.方便动态的扩展功能,切提供了比继承更多的灵活性
缺点
1.多层装饰比较复杂
2.会引入许多小对象,似的应用程序架构变得复杂
什么时候不用
例子
代理模式
适用场景
1.html元素事件代理
2.ES6 proxy
get(target, key, receiver),拦截对象属性的读取,比如proxy.foo和proxy['foo']
set(target, key, value, receiver), 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值
has(target, key), 拦截propKey in proxy的操作,返回一个布尔值
deleteProperty(target, key), 拦截delete proxy[propKey]的操作,返回一个布尔值
ownKeys(target), 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性
getOwnPropertyDescriptor(target, key), 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
defineProperty(target, key, Desc), 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值
preventExtensions(target), 拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target), 拦截Object.getPrototypeOf(proxy),返回一个对象
isExtensible(target), 拦截Object.isExtensible(proxy),返回一个布尔值
setPrototypeOf(target, proto), 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截
apply(target, object, args),拦截Proxy实例作为函数调用操作:proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)
construct(target, args),拦截proxy实例作为构造函数操作:new proxy(...args)
3.jquery.proxy方法
优点
1.代理模式能将代理对象与被调用对象分离,降低了系统的耦合度,代理模式在客户端和目标对象之间起到了中介作用,可以保护目标对象
2.代理对象可以扩展目标对象的功能,通过修改代理对象就可以,符合开闭原则
缺点
处理请求速度可能有差别,非直接访问存在开销
什么时候不用
例子
外观模式
适用场景
1.设计初期,应该要有意识地将不同的两个层分离,比如经典的三层结构,在数据访问层和业务逻辑层、业务逻辑层和表示层之间建立外观Facade
2.在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观Facade可以提供一个简单的接口,减少他们之间的依赖。
3.在维护一个遗留的大型系统时,可能这个系统已经很难维护了,这时候使用外观Facade也是非常合适的,为系系统开发一个外观Facade类,为设计粗糙和高度复杂的遗留代码提供比较清晰的接口,让新系统和Facade对象交互,Facade与遗留代码交互所有的复杂工作
优点
1.减少系统相互依赖。
2.提高灵活性。
3.提高了安全性
缺点
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
什么时候不用
例子
jQuery.ajax
桥接模式
适用场景
优点
有助于独立地管理各组成部分, 把抽象化与实现化解耦
提高可扩充性
缺点
大量的类将导致开发成本的增加,同时在性能方面可能也会有所减少。
什么时候不用
例子
组合模式
适用场景
表示对象-整体层次结构
希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象(方法)
优点
有助于独立地管理各组成部分, 把抽象化与实现化解耦
提高可扩充性
缺点
大量的类将导致开发成本的增加,同时在性能方面可能也会有所减少。
什么时候不用
例子
享元模式
适用场景
如果一个应用程序使用了大量的对象,而这些大量的对象造成了很大的存储开销时就应该考虑使用该模式
文件上传需要创建多个文件实例
优点
大大减少对象的创建降低系统的内存,使效率提高
缺点
提高了系统复杂度,需要分离出外部和内部状态,而且外部状态具有固有化性质,不应该随着内部状态的变化而变化
什么时候不用
例子
0 条评论
下一页