|
什么是设计模式?
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,设计模式并不是一种固定的公式,而是一种思想,是一种解决问题的思路;使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码可维护性。
设计模式不区分编程语言,设计模式是解决通用问题和提效的解决方案;通常在我们解决问题的时候,很多时候不是只有一种方式,我们通常有多种方式来解决;但是肯定会有一种通用且高效的解决方案,这种解决方案在软件开发中我们称它为设计模式;
项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的场景及其原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
什么是设计原则?
设计原则是基于设计模式产生的一套方法论;通常在做很多事情的时候,都会有一定的规范制约;在软件开发的过程中,我们可以将设计原则视为一种约定俗成的开发规范,但不是必须要遵循的;
- 单一职责原则(Single Responsibility Principle)
- 开闭原则(Open Closed Principle)
- 里氏替换原则(Liskov Substitution Principle)
- 迪米特法则(Law of Demeter)
- 接口隔离原则(Interface Segregation Principle)
- 依赖倒置原则(Dependence Inversion Principle)
6 个原则的首字母(里氏替换原则和迪米特法则的首字母重复,只取一个)联合起来就是:SOLID(稳定的),其代表的含义也就是把这 6 个原则结合使用的好处:建立稳定、灵活、健壮的设计;
设计原则
- 单一职责原则(Single Responsibility Principle)
一个类只做一件事;一个类应该只有一个引起它修改的原因,应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。导致脆弱的设计。例如:要实现逻辑和界面的分离
- 开闭原则(Open Closed Principle)
一个软件实体如类,模块和函数应该对扩展开放,对修改封闭,即在程序需要进行拓展的时候,不能去修改原有的代码。
- 里氏替换原则(Liskov Substitution Principle)
不要破坏继承体系;程序中的子类应该可以替换父类出现的任何地方并保持预期不变。所以子类尽量不要改变父类方法的预期行为。
降低耦合度,一个类或对象应该对其它对象保持最少的了解。只与直接的朋友(耦合)通信,不与朋友的朋友通信。
- 接口隔离原则(Interface Segregation Principle)
设计接口的时候要精简单一;当类 A 只需要接口 B 中的部分方法时,因为实现接口需要实现其所有的方法,于是就造成了类 A 多出了部分不需要的代码。这时应该将 B 接口拆分,将类A需要和不需要的方法隔离开来。
- 依赖倒置原则(Dependence Inversion Principle)
细节应该依赖于抽象 ,抽象不依赖于细节;把抽象层放在程序设计的最高层,并保持稳定,程序的细节变化由低层的实现层来完成。(例如实现一个组件基类,存放组件的id、apperance等信息,具体的组件类继承基类,实现具体的UI及功能 )
为什么要使用设计模式?
- 设计模式是前人根据经验总结出来的,使用设计模式,就相当于是站在了前人的肩膀上。
- 设计模式使程序易读。熟悉设计模式的人应该能够很容易读懂运用设计模式编写的程序。
- 设计模式能使编写的程序具有良好的可扩展性,满足系统设计的开闭原则。
- 设计模式能降低系统中类与类(或者function与function)之间的耦合度。
- 设计模式能提高代码的重用度。
- 设计模式能为常见的一些问题提供现成的解决方案。
- 设计模式增加了重用代码的方式。比如装饰器模式,在不使用继承的前提下重用系统中已存在的代码。
设计模式分类
抽象工厂、工厂、单例、建造者、原型
桥接、代理、装饰器、适配器、享元、组合、门面(外观)
- 行为型:观察模板迭代的状态,命令中介解释职责链,访问策略备忘录
观察者、模板、迭代、状态、命令、中介者、解释器、职责链、访问者、策略、备忘录
日常工作常用的设计模式
发布-订阅模式(观察者模式):
定义:
发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知;发布订阅模式有发布订阅调度中心(中间商),观察者模式没有!
应用场景:
DOM元素节点事件实时触发方法
vue的响应式原理
大白话解释(生活中的场景):
假设你在淘宝上看上了某件商品,临近双11,但是价格有点高;于是你订阅了某种类型某种规格的商品降价通知,到双11降价的时候会系统推送相关消息给对应的用户;这时发布-订阅模式就产生了;用户是订阅者,淘宝是调度中心,商家是发布者;商家降价之后,会通过发布中心推送相关的消息给指定的订阅者;
- 1 // 定义一个发布者对象
- 2 var pub = {
- 3 // 缓存列表,存放订阅者回调函数
- 4 list: {},
- 5 subscribe: function (key, fn) {
- 6 // 如果没有该消息的缓存列表,就创建一个空数组
- 7 if (!this.list[key]) {
- 8 this.list[key] = [];
- 9 }
- 10 // 将回调函数推入该消息的缓存列表
- 11 this.list[key].push(fn);
- 12 },
- 13
- 14 // 取消订阅方法
- 15 unsubscribe: function (key, fn) {
- 16 // 如果有该消息的缓存列表
- 17 if (this.list[key]) {
- 18 // 遍历缓存列表
- 19 for (var i = this.list[key].length - 1; i >= 0; i--) {
- 20 // 如果存在该回调函数,就从缓存列表中删除
- 21 if (this.list[key][i] === fn) {
- 22 this.list[key].splice(i, 1);
- 23 }
- 24 }
- 25 }
- 26 },
- 27 // 发布方法
- 28 publish: function () {
- 29 // 获取消息类型
- 30 var key = Array.prototype.shift.call(arguments);
- 31 // 获取该消息的缓存列表
- 32 var fns = this.list[key];
- 33 // 如果没有订阅消息,就返回
- 34 if (!fns || fns.length === 0) {
- 35 return;
- 36 }
- 37 // 遍历缓存列表,执行回调函数
- 38 for (var i = 0; i < fns.length; i++) {
- 39 fns[i].apply(this, arguments);
- 40 }
- 41 }
- 42 }
- 43
- 44 // 定义一个订阅者对象A
- 45 var subA = function (name) {
- 46 console.log('A收到了消息:' + name);
- 47 }
- 48
- 49 // 定义一个订阅者对象B
- 50 var subB = function (name) {
- 51 console.log('B收到了消息:' + name);
- 52 }
- 53
- 54 // A订阅了test消息
- 55 pub.subscribe('test', subA);
- 56 // B订阅了test消息
- 57 pub.subscribe('test', subB);
- 58 // 发布了test消息,传递了参数'hello'
- 59 pub.publish('test', 'hello');
- 60 // A取消订阅了test消息
- 61 pub.unsubscribe('test', subA);
- 62 // 发布了test消息,传递了参数'world'
- 63 pub.publish('test', 'world');
- 64
- 65 // 输出:
- 66 // A收到了消息: hello
- 67 // B收到了消息: hello
- 68 // A取消订阅了test消息
复制代码 代理模式:
定义:
为一个对象提供一个代用品或占位符,以便控制对它的访问!
应用场景:
Proxy代理对象;
图片预加载,占位loading图片就是代理;
大白话解释(生活中的场景):
明星往往拥有很多粉丝,也会接收到很多粉丝送的礼物;但是这些礼物一般都是通过 粉丝 -> 经纪人 -> 明星 这个流程才到达明星的手里;这里的经纪人就是明星的代理对象,有些无用或者恶作剧之类的礼物,可以直接在代理对象(经纪人)这一层直接拦截或者过滤掉; - 1 var fans = {
- 2 flower() {
- 3 agent.reception('花');
- 4 }
- 5 }
- 6
- 7 var agent = {
- 8 reception: function (gift) {
- 9 console.log('粉丝送的:' + gift); // 粉丝送的:花
- 10 if (gift !== '花') {
- 11 star.reception('花');
- 12 }
- 13 }
- 14 }
- 15
- 16 var star = {
- 17 reception: function (gift) {
- 18 console.log('收到粉丝的:' + gift);
- 19 }
- 20 }
- 21
- 22 fans.flower();
复制代码 策略模式:
定义:
定义一系列算法,并将这些算法各自封装成策略类(方法),然后将不变的部分和变化的部分分离开来,并且这些算法可以相互替换
应用场景:
主要用来消除或减少逻辑分支判断,避免冗长的if-else或switch分支判断
大白话解释(生活中的场景):
政府事务办理中心,是近几年来提升居民、市民办事的一项策略调整;在政务中心没出现之前,市民办事都需要去对应的政府机关窗口分流办理相应手续;政务大厅的出现将公安、财务、劳动、税务、供电等多个部门事务办理手续集中到政务中心来办理,这样提高了各级机关单位的办事效率,减少了维护办事人群的秩序,也提升了市民的用户体验;市民只需要记住办理政务相关手续去政务大厅即可;
- 1 /** 策略模式改造前 */
- 2 function calculateBonus(level,salary){
- 3 if(level === 'S'){
- 4 return salary*4;
- 5 }
- 6
- 7 if(level === 'A'){
- 8 return salary*3
- 9 }
- 10
- 11 if(level === 'B'){
- 12 return salary*2
- 13 }
- 14 }
- 15
- 16 console.log(calculateBonus("S",14000)); //56000
- 17 console.log(calculateBonus("A",10000)); //30000
- 18 console.log(calculateBonus("B",5000)); //10000
复制代码- 1 /** 策略模式改造后*/
- 2 var strategies = {
- 3 "S":function(salary){
- 4 return salary*4
- 5 },
- 6 "A":function(salary){
- 7 return salary*3;
- 8 },
- 9 "B":function(salary){
- 10 return salary*2
- 11 }
- 12 }
- 13
- 14 var calculateBonus =function(level,salary){
- 15 return strategies[level](salary);
- 16 }
- 17 console.log(calculateBonus("S",14000)); //56000
- 18 console.log(calculateBonus("A",10000)); //30000
- 19 console.log(calculateBonus("B",5000)); //10000
复制代码 单例模式
定义:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:
弹窗组件;
加载某个script资源或者只能存在一个全局变量;
React状态管理工具Redux
大白话解释(生活中的场景):
假设甲去当地派出所办理身份证相关手续,这时公安机关会对甲的身份进行验证查询,如果甲已经有过身份登记信息,就直接办理挂失补办手续;否则走新增户籍人手续;一个合法公民不可能同时存在两张合法身份证件;
- 1 let Singleton = function (name) {
- 2 this.name = name;
- 3 this.instance = null;
- 4 }
- 5
- 6 Singleton.prototype.getName = function () {
- 7 console.log(this.name);
- 8 }
- 9
- 10 Singleton.getInstance = function (name) {
- 11 if (this.instance) {
- 12 return this.instance;
- 13 }
- 14 return this.instance = new Singleton(name);
- 15 }
- 16
- 17 let Winner = Singleton.getInstance('Winner');
- 18 let Looser = Singleton.getInstance('Looser');
- 19
- 20 console.log(Winner === Looser); // true
- 21 console.log(Winner.getName()); // 'Winner'
- 22 console.log(Looser.getName()); // 'Winner'
复制代码 总结
- 在日常开发中,应该尽可能的遵守设计原则和合理选用设计模式;
- 合理使用设计模式便于我们写出可维护性高、拓展性强的代码;
- 设计模式一共分为三大类,分别是 :5种创建型、7种结构型、11种行为型;
- 设计模式不区分编程语言,设计模式是解决通用问题和提效的解决方案;
- 编程的目的是解决现实问题,同样每一种设计模式的原理都能在现实社会中找到对应的场景,这也是它能被广泛应用的根本原因;
出处:https://www.cnblogs.com/dengyao-blogs/本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
来源:https://www.cnblogs.com/dengyao-blogs/p/17815827.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|