程序设计&编程思想
装饰器模式
修饰器模式/代理模式
核心概念:
为类对象不通过继承的方式 添加的新的功能(方法),而不改变原有的结构和功能
类当中引入修饰器模式
class Parent {constructor() {this.name = 'zhangsan';}//语法@readOnlysay(){}}
问题:ES6的类继承有什么短板?
当它仅仅作为一个功能而不是一个主体时,但是又想继承去使用它的属性和方法,会显得它的功能不够强大,一旦想继承多个功能时,就必须要继承多个类去实现,显得不够灵活,此时可以使用装饰器模式去解决
问题:什么时候会用到装饰器模式?
当某一个类的上方加上@方法名时的装饰器写法可以对其目标target类进行装饰,从而健全这个类所需要的属性和方法,同时,装饰器不仅可以修饰类,也可以修饰函数
问题:装饰器有什么好处?
不用继承就可以使用公共的属性和方法(新增附加功能),它也是一个纯函数,也不用传参,使用的时候非常的灵活(在任何地方也可以使用),也可以实时无数个装饰器去装饰一个类
问题:如何写一个装饰器函数?
定义一个函数,里面有三个参数:
target: 目标类key:键名descriptor:描述符
export default(target) => {target.plus = function(num1, num2){return num1 + num2;}target.minus = function(num1, num2){return num1 - num2;}...}
注意:使用装饰器写法时,得在项目中新增
es7依赖
迭代器模式
问题:什么是遍历?
它实际上是从一个容器里拿到所有的东西出来,它的缺点是不能对每一次遍历的过程中对遍历的结果进行处理
问题:什么是迭代?
在本次遍历的过程中进行一次程序上的输出,它仅仅是遍历中的其中一步
问题:什么叫项目迭代?
根据本次的结果进行一次更新(修复),或者是小功能的叠加,在遍历过程中进行某一次输出时,必须用到迭代器
问题:迭代器有什么作用?
它可以避免遍历一次性的执行全部(如for of,for循环),从而一次一次的去执行
问题:迭代器的缺点?
不能单独使用,它建立在遍历的基础之上,需要结合生成器使用
问题:生成器是什么?
它是生产迭代器(iterator)的函数,利用遍历生产一个迭代器function * test(){},函数内部的yield关键字可以暂停遍历,并可以加工处理
关于迭代器yeild关键字:
它可以在遍历时暂停迭代操作
关于迭代器自带的next方法:
它调用时会执行一次迭代操作,迭代没有完成时,它返回的是一个对象{value: 1, done: false},完成时,它返回一个对象{value: undefined, done: true}
//es6写法:function * test(arr){for(var item of arr){yield item;}}let iterator = test(arr);console.log(iterator.next());console.log(iterator.next());...
//es5写法: 不用*写法function generator(arr){var i = 0;return {next: function(){//最后一项时为truevar done = (i >= arr.length),//非最后一项时为当前项 并i++//最后一项时为undefinedvalue = !done ? arr[i++] : undefined;return {value: xxx,done: false}}}}
应用场景:
如底层封装时经常用到,如打印日志等操作,它具有记忆功能
//场景1var functions = [function test1(){},function test2(){ return false },function test3(){},]//当某一个函数返回false时中止遍历for(let item of functions){if(!item()){break;}}
//场景2 koa中的洋葱模型中间件var fns = [function test1(next) {console.log('test1');next();},function test2(next) {console.log('test2');// next();},function test3(next) {console.log('test3');next();}];//当某一个函数没有写next()执行时迭代中断function* generator(arr) {for (let i = 0; i < arr.length; i++) {//中止每次遍历出来的元素yield arr[i];}}const iterator = generator(fns);//实现全部函数都执行nextDo(iterator.next());function nextDo(n) {// console.log(n);//{done: false, value: ƒ test1(next)}n.value(function () {const n = iterator.next();// console.log(n);//{done: false, value: ƒ test2(next)}//未迭代完成时if (!n.done) {nextDo(n);} else {//迭代完成时return;}});}
观察者模式
案例:购物车(课程列表)
页面包含课程列表和待报名课程两个模块
实现:
点击课程列表其中的项目后会与待报名课程模块立即会有联动(一个模块里有两个功能模块)
问题1:为什么需要两个子模块分开处理?
因为如果两个模块一起编写,在修改其中模块时会影响另一个模块,而且不方便维护,且逻辑之间杂乱不清
问题2:两个分开模块之间如何实现数据联动管理?
利用观察者模式实现管理数据更新
观察者原理:
每一个观察者实际上是一个函数,一旦触发事件,执行一个函数完成一个程序,当一个程序分为1,2,3件事情,那么这个程序就为一个observers,当执行程序123(observers)时不能单独执行,利用obsevers里面的notify通知特性,将123一次性执行,真正事件触发时仅仅执行的是notify函数
也就是说,希望123函数作为一个个obsevers放入数组里,统一用notify一次去执行,在项目里,点击单击事件时触发执行notify函数,然后notify里面所有的observers会依次执行
//Observers.jsobservers = [fn, fn, fn];add() -> fn -> observers//执行notify()会使每一项observer执行notify() -> forEach -> observers -> item();
项目渲染原理:
数据 (需要处理) -> 容器 -> 渲染 -> 插入节点
观察者管理的好处:
一个notify函数的执行同时 把handle底下的所有函数同时执行,实现参数实时共享 好处是模块功能同时联动,实现高内聚的代码编写
项目结构:
├─package.json├─webpack.config.js├─src| ├─index.html| ├─utils| | ├─Observer.js - 观察者类 驱动| | └tools.js - 工具类方法集合| ├─templates - 模板文件| | ├─cartItem.tpl| | └courseItem.tpl| ├─js| | ├─index.js - 网页入口文件| | ├─ShoppingCart| | | ├─index.js - 主程序入口文件| | | ├─Course| | | | ├─Event.js - 管理子模块的绑定事件| | | | ├─Handle.js - 管理子模块里绑定事件函数底下的所有逻辑方法| | | | ├─index.js - 子模块入口文件| | | | └Render.js - 渲染函数| | | ├─Cart| | | | ├─Event.js| | | | ├─Handle.js| | | | ├─index.js| | | | └Render.js| ├─data - 数据| | ├─cart.js| | └course.js
源码地址:
https://gitee.com/kevinleeeee/data-response-observers-shoppingcart-demo
工厂模式
案例:模态框
使用工厂设计模式做一个插件库里面有一个模态框组件(仅展示设计模式用)
技术:
express+ 原生js+ 工厂模式
功能:
- 按钮1:成功态
- 按钮2:警告态
- 按钮3:失败态
问题:工厂设计解决什么问题?
希望有一个模块ModalFactory类去可以根据状态的不同去实例化创建,并且实现点击切换样式颜色功能的同时还可以拓展其他功能,便于日后维护
继承关系:
Modal <- extend - SuccessModal/WarningModel/ErrorModalModalFactory -> new SuccessModal/WarningModel/ErrorModal -> 拿到实例对象 -> 实例对象.create(标题, dom节点自定义属性)
项目目录: https://gitee.com/kevinleeeee/factory-design-ui-plugin-modal-demo
状态模式
将一个类抽象多个状态的类,这些类和主类保持了继承关系和依赖关系
问题:什么是solid设计原则?
- 单一职责原则(胖函数减肥)
- 开放封闭(拓展开发修改)
- 里氏替换
- 接口开放
- 依赖倒置
享元模式
享受元模式(fly weight),它是解决性能优化的有效射击模式,通过共享属性状态(属性和方法)的方式来达到减少创建对象的目的,以此来提升性能优化
状态管理器:
一个对象方法专门来负责添加内部(实例添加)和外部状态(动态赋值)
问题:为什么区分内部和外部状态?
内部状态通过工厂(Factory)的方式来创建,外部状态通过构造函数(Manager)的方式来设置
关于单例模式:、
一种方式,函数执行完毕时返回一个实例
问题:享元模式如何实现性能优化?
它循环添加内部状态时都像单例模式那样返回同一个实例以达到性能优化
派发器模式
Dispatcher派发器模式来改造组件的逻辑部分
问题:如何的去根据需求去更改数据?
methods操作数据 data,更多的不想逻辑写在methods里面,因为一个组件它的逻辑部分非常多的话,methods里面就不好维护了
优点是给更多同步开发的开发者实现方法管理
问题:如何根据一个模式去设计不让method里面的逻辑变得臃肿?
抽离相应的部分,通过type找到一个对应事件,找到事件对应的逻辑,逻辑通过事件类型type去触发派发器,触发数据的改变,这样就横向的把methods拆分出来了
原理:
type -> 事件 -> 逻辑 -> type -> 派发器 -> 数据的更改
//dispatchers > xxx(组件名称).jsexport default (ctx) => {const {method,removeItem,changeCompleted} = todoReducer(ctx.todoData);return function (type, arg) {switch (type) {case ADD:ctx.todoData = addItem(arg);break;case REMOVE:ctx.todoData = removeItem(arg);break;case COMPLETED:ctx.todoData.completed = changeCompleted(arg);break;default:break;}}}
//reducer > xxx(组件名称).jsfunction todoReducer(data) {function addItem(newItem) {return data.concat(newItem);}function removeItem(id) {return data.filter(item => item.id !== id);}function changeCompleted(id) {return data.map((item) => {// console.log(item);if (item.id === id) {item.completed = !item.completed;}});}return {addItem,removeItem,changeCompleted}}export default todoReducer;
//actions > xxx(组件名称).jsconst ADD = 'ADD',REMOVE = "REMOVE",COMPLETED = 'COMPLETED';export {ADD,REMOVE,COMPLETED}
//index.vue块组件methods: {dispatch(...args) {dispatch(this)(...args);}},
案例 1:通过加减更改结果
//项目结构├─package.json├─webpack.config.js├─src| ├─App.vue| ├─main.js| ├─reducers| | └counter.js - 所有couter组件的的业务逻辑都在这里进行管理| ├─dispatchers| | └counter.js - 派发器导出接收一个组件上下文的函数/定义methods里面的许多方法| ├─components| | ├─Counter| | | ├─Button.vue| | | ├─index.vue - 块组件管理组件/使用派发器| | | └Result.vue| ├─actions| | └counter.js - 专门管理counter组件里面事件的类型 如PLUS/MINUS├─public| └index.html
源码地址:
https://gitee.com/kevinleeeee/dispatcher-vue2.x-demo1
案例 2:计算器
技术:派发器
//项目结构├─src| ├─App.vue| ├─main.js| ├─reducers| | └calculator.js| ├─libs| | └utils.js| ├─dispatchers| | └calculator.js| ├─components| | ├─Calculator| | | ├─CalButton.vue| | | ├─CalInput.vue| | | ├─CalResult.vue| | | └index.vue| ├─actions| | └calculator.js├─public| └index.html
源码地址:
https://gitee.com/kevinleeeee/dispatcher-calculate-vue2.x-demo2
案例 3:Todolist
//项目结构├─src| ├─App.vue| ├─main.js| ├─reducers| | └todoList.js| ├─dispatchers| | └todoList.js| ├─components| | ├─TodoList| | | ├─index.vue| | | ├─TdForm.vue| | | ├─TdTitle.vue| | | ├─TdList| | | | ├─index.vue| | | | └ListItem.vue| ├─actions| | └todoList.js├─public| └index.html
源码地址:
https://gitee.com/kevinleeeee/dispatcher-todolist-vue2.x-demo3
