什么是设计模式

设计模式是一种应对多场景或者复杂的业务场景的一种工具,有利于代码的复用/维护,系统模块的组织和设计的沟通

设计模式五大原则

开闭原则

开闭原则(OCP: open closed principle) - 对拓展开放、对修改关闭
开闭原则的设计思路是:在我们系统已有的场景下,对于需要拓展的功能进行开放,拒绝直接的功能修改
exm:
假如现在有PUBG和LOL两个游戏,我们需要在LOL弹出一个充值折扣的功能,PUBG需要一个高亮的功能(功能随意,只是为了demo的理解而写)
首先看一个一下就就能想到的万能if写法

if(game === 'PUBG') {// 高亮
} else {// ……
}// event
if(game === 'LOL') {// 弹出折扣框
} else {// 付款
}

这种写法虽然很好理解,但对于代码维护和可复用性来说无疑是非常差的,那么针对于开闭原则,可以做以下改造

class Game {//基础类 进行基础方法定义constructor (name) {this.name = name;}setColor(){console.log('color');}openDialog(){console.log('付款');}
}class LOL extends Game {openDialog(){//我需要基础的方法的部分功能,但有一些又不能满足我的需求,于是我给他重写了console.log('折扣');}
}class PUBG extends Game {openDialog(){console.log('付款');}setColor(){console.log('高亮');}
}class DNF extends Game {/*** 假如以后我又有一款游戏,这个游戏只需要基础的模式即可,那么我只需要创造出来* 就自带了基础的功能,如果我想要添加新的功能,直接在这个游戏类里面进行添加/修改*/
}

这个例子就说明了OCP的设计思想,对于一个系统/模块,在很多情况下存在同样功能的时候,统一管理核心功能模块并进行维护即可,对于个别派生出来的模块需要有别的功能,添加/修改即可,不会影响到核心

单一职责原则

单一职责原则(SRP) - 通过解耦让每一个职责更加的独立,职责单一,互不重叠
exm: 要在游戏里面有一个弹框的功能,以下是基础类

class PUBGManager {openDialog() {// 弹框// 计算金额setPrice();}
}const game = new PUBGManager();
game.openDialog(); // 弹框 < = > 计算金额 两个模块耦合

很明显,这样的写法达到功能需求是可以的,但是如果到了code review的时候,不难发现,弹框和计算金额两个模块耦合了,那么如何进行重构/优化呢?

// game库 (前置功能,拿到游戏的管理器)
function gameManager(game) {return `${game}Manager`;
}// gameManager.js - 游戏管理器
class PUBGManager {//通过命令修改价格constructor(command){//command可以理解为后续拿到的金额设置模块的一个实例//也是一种设计模式(命令模式),我的操作对象不是元素/模块本身,而是命令this.command = command;}openDialog(price){this.command.setPrice(price);}
}// optManager.js - 底层库
class PriceManager {setPrice(price){//配置金额}
}// main.js
const exe = new PriceManager();
const game = new PUBGManager(exe);
game.openDialog(15);
// game.setPrice(10); // 若需求需要直接调用可增加该模块

这样做看起来就没有那么耦合了,因为我把功能抽离了来,游戏也成了统一的管理,每一个文件管理一个大类,而每一个模块就做专一的一个小事,从而达到了解耦的目的

依赖倒置原则

依赖倒置原则(DIP):上层不应依赖底层实现,也就是我们的设计应该依赖于接口的抽象,而不依赖于具体的实现
exm 需要一个分享功能,老规矩,先来一个反面教材

// 分享功能
class Store {constructor() {this.share = new Share();}
}class Share {shareTo() {// 分享到不同平台}
}const store = new Store();
store.share.shareTo('wx');//这个时候需要增加一个功能
// 评分功能
class Store {constructor() {this.share = new Share();this.rate = new Rate();//问题就在这里}
}class Share {shareTo() {// 分享到不同平台}
}class Rate {star(stars) {// 评分}
}const store = new Store();
store.rate.stars('5');

这样固然是实现了功能,那么假如我现在有一个大的版本迭代呢,需求要加很多功能模块,那我就需要在constructor里面去新增挂载,显然,这样的设计是不好的,那么正面教材又来了

// 目标: 暴露挂载 => 动态挂载
class Rate {init(store) {store.rate = this;}store(stars) {// 评分}
}class Store {// 维护模块名单static modules = new Map();constructor() {// 遍历名单做初始化挂载for (let module of Store.modules.values()) {module.init(this);}}// 注入功能模块static inject(module) {Store.modules.set(module.constructor.name, module);}
}class Share {init(store) {store.share = this;}shareTo(platform) {// 分享到不同平台}
}// 依次注册完所有模块
const rate = new Rate();
Store.inject(rate);// 初始化商城
const store = new Store();
store.rate.star(4);

这样就实现了一个动态挂载的功能,实例在新建的时候就已经有了所需的功能,有了功能其实只需要调用游戏库里面注入的功能的方法,就可以把模块挂载到游戏库里面了

接口隔离原则

假设一个场景,有一个接口有我们需要的某个或者某几个功能,但是我们每次run都会跑接口所有的功能模块,这样的接口一般称为胖接口,那么接口隔离原则即是对接口进行分组管理,拆分模块
接口隔离原则(ISP):多个专业的接口比单个胖接口好用
exm: 现在我已经可以开发游戏了,但是需要实现游戏中台 - 快速生产游戏

class Game {constructor(name) {this.name = name;}run() {// 跑}shot() {// 开枪}mega() {// 开大}
}class PUGB extends Game {constructor() {// pubg constructor}
}class LOL extends Game {constructor() {// lol constructor}
}pubg = new PUBG('pubg');
pubg.run();
pubg.shot();
pubg.mega();

那么这个例子一看就看出毛病了:谁家的PUBG还能开大?那么接下来就是改造环节了

// 用多个接口替代他,每个接口服务于一个子模块
// 瘦身
class Game {constructor(name) {this.name = name;}run() {// 跑}
}class FPS {}class MOBA {}class PUGB extends Game {constructor() {// pubg constructor}shot() {}
}class LOL extends Game {constructor() {// lol constructor}mega() {}
}

那么这样的话就实现了每个接口/功能模块专注于做自己的事情

里氏替换原则

里氏替换原则(LSP:the Lxxxx substitution principle):子类能够覆盖父类,父类能够出现的地方子类就能出现,子类可以扩展,不能改变
exm: 现在就是要把游戏分为pc端和mobile端来做

class Game {start() {// 开机逻辑}shutdown() {// 关机}play() {// 游戏}
}const game = new Game();
game.play();// sprint 2
class MobileGame extends Game {tombStone() {// tombStone}play() {// 移动端游戏//这里的play覆盖掉了父类的play}
}
const mobile = new MobileGame();
mobile.play();

问题来了MobileGame 的play实际上覆盖掉了父类的play,这样直接去覆盖属性其实是不好的,那么下面就是基于里氏替换原则的改进

class Game {start() {// 开机逻辑console.log('start');}shutdown() {// 关机console.log('shutdown');}
}class MobileGame extends Game {tombStone() {console.log('tombStone');}play() {console.log('playMobileGame');}
}class PC extends Game {speed() {console.log('speed');}play() {console.log('playPCGAME');}
}

不难发现,父类已经不再具有play的方法了,我把play下发到子类去进行自我实现,这样也就体现出了“子类能够覆盖父类,父类能够出现的地方子类就能出现”的功能,里氏替换原则更多的关注的是代码的可维护性,可长期迭代性

最后

以上就是设计模式的五大原则,下机

关于设计模式五大原则相关推荐

  1. 设计模式原则(单一、开放封闭、里氏代换、依赖倒转、迪米特法则五大原则)...

    原文:设计模式原则(单一.开放封闭.里氏代换.依赖倒转.迪米特法则五大原则) 单一职责原则 单一职责原则,就一个类而言,应该仅有一个引起它变化的原因.   如果一个类承担的职责过多,就等于把这些职责耦 ...

  2. PHP面向对象设计的五大原则

    面向对象设计的五大原则:单一职责原则.接口隔离原则.开放-封闭原则.替换原则.依赖倒置原则.这些原则主要是由Robert C.Martin在<敏捷软件开发--原则.方法.与实践>一书中总结 ...

  3. solid 设计原则 php,面向对象设计SOLID五大原则

    今天我给大家带来的是面向对象设计SOLID五大原则的经典解说. 我们知道,面向对象对于设计出高扩展性.高复用性.高可维护性的软件起到很大的作用.我们常说的SOLID五大设计原则指的就是: S = 单一 ...

  4. 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...

  5. 软件开发、设计、架构的五大原则

    唯一不变的就是变化本身. 我们经常讲的系统.子系统.模块.组件.类.函数就是从逻辑上将软件一步步分解为更细微的部分,即逻辑单元, 分而治之, 复杂问题拆解为若干简单问题, 逐个解决. 逻辑单元内部.外 ...

  6. C++设计模式之二(设计模式六大原则)

    精彩博客:https://www.cnblogs.com/dolphin0520/p/3919839.html 补充一下面对对象设计八大原则:前五大原则与设计模式的前五大原则相同,为: 1.单一职责原 ...

  7. java五大原则_Java成长第五集--面向对象设计的五大原则

    S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.以下图说明: 下面就个人的理解来说说这五大原则的含义到 ...

  8. PHP 设计模式六大原则

    http://www.cnblogs.com/yujon/p/5536118.html 设计模式六大原则(1):单一职责原则 不要存在多于一个导致类变更的原因.通俗的说,即一个类只负责一项职责 设计模 ...

  9. 面向对象思想 常说的OOP五大原则就是指1、单一职责原则; 2、开放闭合原则; 3、里氏替换原则; 4、依赖倒置原则; 5、接口隔离原则。...

    常说的OO五大原则就是指其中的 : 1.单一职责原则: 2.开放闭合原则: 3.里氏替换原则: 4.依赖倒置原则: 5.接口隔离原则. https://blog.csdn.net/Anders_Zhu ...

最新文章

  1. Blender创作你自己的动画短片学习教程
  2. 1282. Game Tree
  3. lambda表达式_在Java 7或更早版本中使用Java 8 Lambda表达式
  4. Glut处理鼠标事件:函数glutMouseFunc
  5. Windows下Unity5x的下载
  6. exchange 2010 集线器(hub)外发邮件的配置
  7. PHP之Smarty简单实现
  8. leetcode695. 岛屿的最大面积(dfs)
  9. 解决el-select后台一次返回大数据量渲染慢导致页面卡顿的问题
  10. IDEA导入项目笔记
  11. 【Flink】Flink 消费 kafka报错 写入 es 死信队列 FetchSessionHandler DisconnectException
  12. centos 6.5卸载Mysql
  13. 征途2助手网关服务器,【征途2】官方网站-《征途2》夏日激情来袭,清凉一夏版本送限定套装...
  14. stm32芯片超时无应答解决
  15. 手机显示无法接通服务器怎么办,手机无法接通是什么原因及如何解决【图文】...
  16. python使用wxpy轻松实现微信防撤回
  17. G代码表《G代码大全》
  18. 强烈推荐这个Java学习文档——不看后悔系列
  19. 台式win10怎么开启无线服务器,无线网卡怎么用? win10系统台式机安装无线网卡的详细教程...
  20. 无法加入域--隶属于域、工作组选项为灰色

热门文章

  1. Java+OpenCV图片对比
  2. 即时通讯音视频解决方案 音视频技术处理
  3. 如何更改图片的分辨率
  4. mysql高可用架构_mysql高可用架构详解
  5. 何时用put,get,post
  6. 怎样设置计算机保护密码,怎样电脑设置密码保护
  7. JavaScript foreach 方法跳出循环
  8. UVA-1598 交易所 题解答案代码 算法竞赛入门经典第二版
  9. 【VFB】程序(VFB教程2-7)
  10. UDP-GalNAc,CAS:108320-87-2,UDP-N-acetylgalactosamine