设计模式六大原则

软件开发过程中常用的六大设计原则 单一职责原则(Single Responsibility Principle) 定义 不要存在多余一个导致类变更的原因 解释 一个类只负责一项职责 作用 根据业务功能分离代码模块 分析 单一职责原则非常简单,稍微拥有编程经验的人都会去遵守单一职责原则,即使不知道该原则的存在。但是在实践过程中,单一职责原则会面临最大的敌人——职责扩散 所谓职责扩散,指的是由于某些原因,职责 P 被划分为粒度更细的职责 P1, P2, …等,这种情况,我们需要根据实际情况来决定遵循单一职责原则的粒度。 如果一个职责的扩散不可控,那么必须在职责扩散失控前,重构代码 里氏替换原则(Liskov Substitution Principle) 定义 如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型 所有引用基类的地方必须能透明地使用其子类的对象 解释 子类要能够完成父类所有的职责,在这样的一个系统中,基类(祖先类)的对象,被其子类对象替换后,整个系统工作不会受到影响。 ::子类可以扩展父类,但是不能修改(重写)父类的方法:: 作用 提高系统稳定性、可扩展性 分析 在面向对象编程过程中,里氏替换原则要求我们,在子类里面不要重写/修改父类中已经实现的方法(抽象方法除外) 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。 子类中可以增加自己特有的方法。 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。 接口隔离原则(Interface Segregation Principle) 定义 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上 解释 类 A 所依赖的接口,应该只包含类 A 用到的方法 作用 避免无用代码,防止类过于臃肿 分析 接口隔离原则,在于控制接口的粒度,如果一个接口中包含过多的方法,那么在实现类中就需要实现所有方法,就会导致代码非常臃肿。这个时候就需要将接口拆分为粒度更细的多个接口。...

September 18, 2019 · 1 min · 116 words · Johnson

GitHub竟然还可以这样玩

细心的人都会发现 GitHub 个人主页有一个记录每天贡献次数的面板,我暂且称之为贡献面板。就像下图那个样子。只要当天在 GitHub 有提交记录,对应的小格子就会变成绿色,当天提交次数越多,颜色也会越深。因此我就有了一个大胆的想法。细心的你应该也发现了,我就是要讲如何搞出这个小 ❤️❤️ 来。项目地址:https://github.com/YES-Lee/git_painter 原理 基本原理前面已经讲过,我们只需要控制项目提交的日期和次数,就能在贡献面板中填充出花样来。可能有朋友会问,前面的部分怎么办?拿到我得等一年?No☝️,经过研究发现,GitHub 的贡献面板是活得,什么意思呢?GitHub 是根据项目的提交记录实时生成的贡献面板,所以只要在本地把时间改成过去,进行提交操作,再 push 到 GitHub,就实现了穿越。了解了这些之后,就可以动手了。最后如果效果不尽人意或者像换个图样换个心情,只需要删了对应的仓库就行了。 坑 1:码云不像 GitHub,码云的贡献面板是一次性的,推上去之后就不会变,所以谨慎操作 程序设计 目标 设计一个模板,可以通过修改模板来改变图样 将过去时间的记录自动全部填充 每天进行自动COMMIT/PUSH操作 实现 模板设计成一个json二维数组,由0和1组成,分别到表有/无提交记录。行数最好固定是 7(周一到周日),列数可以自已随意设置。下面是基于python的实现。 model.json [ [0, 1, 1, 0, 0, 0, 1, 1, 0, 0], [1, 1, 1, 1, 0, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0] ] 下面的代码是根据当前日期和模板对应的值来进行提交,用于定时任务每天执行。可以直接部署到服务器,通过后面的命令设置定时任务。 main....

July 20, 2018 · 3 min · 536 words · Johnson

原型、原型链、继承

JavaScript 是一个基于原型链继承的面向对象编程语言。在继承的实现上和 Java 的等经典面向对象编程语言有很大的区别,在 ES6 中也加入了class关键字的支持,但是本质上就是一个语法糖。基于原型链的继承让 JavaScript 的继承实现更加灵活,这也是 JavaScript 必须掌握的基础知识。 原型 JavaScript 中每一个对象都有原型(也可以创建一个没有任何原型的对象),当使用new关键字实例化一个对象,该对象的原型也就指向了构造器的prototype属性。访问对象的属性的时候,首先会查找对象自身,如果该属性不存在,则会去查找对象的原型。这样,就可以将需要继承的属性放到原型对象中,让其所有子类都能够访问。 function Book(name) { this.name = name } Book.prototype.getName = function() { return this.name } const book = new Book('JavaScript') book.getName() // JavaScript 显式原型和隐式原型 JavaScript 的原型分为显式原型和隐式原型,两者之间最直观的区别是:显式原型是函数(构造器)的原型属性prototype,而隐式原型是所有对象实例的原型属性__proto__,即new Book().__proto__ === Book.prototype。 原型链 当访问对象属性的时候,首先回去查找对象自身是否包含该属性,如果没有,则会去__proto__中查找,如果__proto__中也不存在,则会继续去__proto__.__proto__中查找,直到访问到最顶层__proto__也就是Object.prototype。这一个过程就是通过原型链进行访问,一层一层的原型串成了一条链。 Object的原型对象没有原型,Object.prototype.__proto__为 null。 hasOwnProperty 和 in 如果要判断一个对象是否拥有某个属性的时候,可以使用Object.prototype.hasOwnProperty方法和in关键字。两者的区别在于,hasOwnProperty只会去查找对象自身是,不会去查找原型链,而in关键字会去查找原型链。 const book = new Book('JavaScript') book.hasOwnProperty('name') // true book.hasOwnProperty('getName') // false 'getName' in book // true 继承 JavaScript 的继承是通过原型实现的,可以通过给原型设置特定的属性,让其实例能够访问到(继承) const Book = { getName() { return this....

February 1, 2018 · 3 min · 438 words · Johnson

C语言编译基础知识

C 语言编译基础知识,C 语言从源码到可执行程序总共经历了预处理、汇编、编译、链接 4 个阶段。 编译过程 源代码->预处理->汇编->编译->链接->可执行程序 预编译 指令:gcc -E 产出:xxx.i(预编译文件) 工作:处理源代码中#开头的部分,将头文件拷贝至源码中,对#define宏定义进行文本替换,去掉注释 汇编 指令:gcc -S 产出:xxx.s(汇编源文件) 工作:将预编译文件翻译成汇编语言 编译 指令:gcc -c 产出:xxx.o(目标文件) 工作:将汇编代码翻译成机器语言(二进制) 链接 指令:gcc -o 产出:可执行文件(.out) 工作:将目标文件链接在一起合成可执行文件,如有多个源文件,需要指定链接文件 编译指令 编译环境:macOS 10.14.5 编译器:Apple LLVM version 10.0.1 (clang-1001.0.46.4), Target: x86_64-apple-darwin18.6.0 常用指令 gcc -I dirname:搜索dirname目录下的头文件,在预编译阶段使用 gcc -L dirname: 搜索dirname目录下的目标(库)文件,在链接阶段使用 源代码: // 头文件 ./lib/mymath.h #ifndef _MYMATH_H #define _MYMATH_H int sum(int a, int b); #endif // 实现文件 ./lib/mymath.c #include "mymath.h" int sum(int a, int b) { return a + b; } // main....

1 min · 123 words · Johnson

vue权限控制

在 SPA(单页面应用)中,前端需要根据用户的权限来控制用户菜单以及路由表,vue-router提供了几个路由生命周期钩子,叫做路由守卫,我们可以利用路由守卫在路由以及路由元信息进行权限控制,同时搭配 vuex 将会更美味,文末有完整示例地址。 登录权限 登录验证是最常见的一种路由权限验证,使用vuex + 路由守卫可以实现比较清晰流畅的鉴权流,能轻松应对页面刷新、清除缓存等场景。 路由元信息 vue-router在构建路由时提供了元信息meta配置接口,我们可以在元信息中添加路由对应的权限,然后在路由守卫中检查相关权限,控制其路由跳转。 // router.js // 路由表元信息 ;[ { path: '', redirect: '/home', }, { path: '/home', meta: { title: 'Home', icon: 'home', }, }, { path: '/userCenter', meta: { title: '个人中心', requireAuth: true, // 在需要登录的路由的meta中添加响应的权限标识 }, }, ] // 在守卫中访问元信息 function gaurd(to, from, next) { // to.matched.some(record => record.meta.requireAuth) // 可在此处 } vuex 一般的,用户登录后会在本地持久化存储用户的认证信息,本文以JWT的token为例。将用户的 token 持久化到 localStorage 里,而用户信息则存在内存(store)中。这样可以在 vuex 中存储一个标记用户登录状态的属性auth,方便用语权限控制。 // store.js { state: { token: window....

2 min · 349 words · Johnson