这是我参与「第四届青训营 」笔记创作活动的的第4天!
前端设计模式应用(基础原理篇)
1.什么是设计模式
设计模式是软件设计中常见问题的解决方案模型。
历史经验的总结、与特定语言无关
2.设计模式分类
设计模式共有28种,根据模式的共同点,我们可以将设计模式分为三大类:
- 创建型
- 结构型
- 行为型
2.1-设计模式——创建型
对对象的实例化过程进行抽象,即如何创建一个对象。
对于类创建模式来说通过使用继承来改变实例化的类。
对于对象创建模式来说通过使用代理来实例化所需的对象。
创建型模式(5种)有:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
2.2-设计模式——结构型
通过对多个类和对象进行组合得到复杂结构的类,即如何灵活的将对象组装成较大的结构。
使用继承或者成员变量引用形式来实现。
结构型模式(7种)有:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
2.3-设计模式——行为模式
行为模式不仅表达了对象和类,还表达了他们之间的交互,即负责对象间的高效通信和职责划分。
结构型模式(11种)有:
-
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 -
两个特殊模式:并发型模式和线程池模式。
3.六大原则
六大原则:开闭原则(总原则)、单一职责原则、里氏替换原则、依赖倒转原则、接口隔离原则、迪米特原则、合成复用原则。
3.1-总原则—开闭原则
开闭原则是编程中最基础、最重要设计原则
- 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
- 对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。
3.2-单一职责原则
一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。
优点:
- 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
- 提高类的可读性。复杂性降低,自然其可读性会提高。
- 提高系统的可维护性。可读性提高,那自然更容易维护了。
- 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。
3.3-里氏替换原则
子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。通俗理解就是子类可以扩展父类的功能,但不能改变父类原有的功能。里氏替换原则是对开闭原则的补充。
优点:
1. 约束继承泛滥,它也是开闭原则的一种很好的体现。
2. 提高了代码的重用性。
3. 降低了系统的出错率。类的扩展不会给原类造成影响,降低了代码出错的范围和系统出错的概率。
4. 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。
3.4-依赖倒转原则
依赖倒转的中心思想是:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
3.5-接口隔离原则
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下5个优点。
(1)将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
(2)接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
(3)如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大, 灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
(4)使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
(5)能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。
3.6-迪米特原则
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
3.7-合成/聚合复用原则
合成/聚合复用原则是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。尽量首先使用合成/聚合的方式,而不是使用继承。即首先考虑作为成员变量来调用另一个类的方法。
4.浏览器中的设计模式
浏览器中常用的两种设计模式:单例模式和发布订阅模式。
4.1-单例模式
- 定义:全局唯一访问对象
- 应用场景:缓存、全局状态管理等。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
单例类只能有一个实例。
单例类必须给自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一个实例。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点:
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
4.2-发布订阅模式
- 定义:一种订阅机制,可在被订阅对象发生变化时通知订阅者。
- 应用场景:从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅、线上订阅等等,应用广泛。
发布—订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
优点:
- 耦合性低,便于代码的维护
缺点:
- 创建订阅者本身要消耗一定的时间和内存,可能订阅的消息未发生,但这个订阅者会始终存在于内存中
5.JavaScript中的设计模式
JavaScript中的设计模式有三种:原型模式、代理模式、迭代器模式。
5.1-原型模式
- 定义:复制已有对象未创建新的对象。
- 应用场景:JS中对象创建的基本模式。
优点:
- Java 自带的原型模式基于内存[二进制]流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
原文链接:blog.csdn.net/weixin_4359…
5.2-代理模式
- 定义:可自定义控制原对象的访问方式,并且允许在更新前后做一些额外处理。
- 应用场景:监控、代理工具、前端框架实现等。
代理模式有不同的形式主要有三种
- 静态代理
- 动态代理 (JDK 代理、接口代理)
- Cglib 代理
5.2.1-静态代理
静态代理在使用时需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
优点:
- 在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展。
缺点:
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。一旦接口增加方法,目标对象与代理对象都要维护
5.2.2-动态代理
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
5.2.3-Cglib代理
- 它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理
- 它一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口
- 它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
- 它是通过底层使用字节码处理框架 ASM 来转换字节码并生成新的类
原文链接:blog.csdn.net/weixin_4585…
5.3迭代器模式
- 定义:在不暴露数据类型的情况下访问集合中的数据
- 应用场景:数据结构中有多种数据类型,列表、树等,提供通用操作接口。
- 迭代器模式的本质:控制访问聚合对象中的元素。其设计意图:无须暴露聚合对象的内部实现,就能够访问到聚合对象中的各个元素。
优点:
- 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计;
- 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求;
- 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性;
- 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
原文链接blog.csdn.net/weixin_4214…
6.前端框架中的设计模式
前端框架中的设计模式有两种:代理模式(上面已有详细知识)和组合模式。
6.1-组合模式
- 定义:可对多个对象组合使用,可也单个对象独立使用。
- 应用场景:DOM、前端组件、文件目录、部门。
组合多个对象形成[树形结构]以表示“整体-部分”的关系的层次结构。组合模式对叶子节点和容器节点的处理具有一致性,又称为整体-部分模式。
优点:
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。
- 客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。
- 定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
- 更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
缺点:
- 使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联
7.个人总结
这是继HTML、CSS和JS后学习前端的第4天,今天主要讲的是前端设计模式和应用,我主要学习了设计模式的定义、方法和应用场景。对于我来说,我的个人基础比较差,对一些设计模式的原理不是很清楚,所以我根据老师上课所说的知识+课外查询的资料整理成了以上这篇文章,主要针对前端需要涉及到的一些设计模式基础原理,让大家能了解到基础知识,希望能帮助到大家,谢谢!