什么是设计者模式

  • 设计模式是用于解决某个问题的一种固定模式(原理都是一样的),不区分语言。
  • 常用的设计模式有23种,主要分为三大类:创建型、结构型、行为型

设计模式分类

  • 创建型 (创建对象):单例模式、工厂模式
  • 结构型(将多个小结构并入一个大结构):组合模式、代理模式、装饰器模型
  • 行为型 (对应类和对象的行为进行相关处理):观察者模式

集中式架构技术有哪些 集中式设计_Vue

设计模式的原则

  • 开闭原则:软件实体 (类、模块、函数等等) 应该是可以被扩展的,但是不可被修改
  • 里式置换原则:所有引用基类的地方必须能透明地使用其子类的对象
  • 单一职责原则:一个类只负责一项职责
  • 依赖倒置原则:依赖于抽象,不依赖于具体
  • 接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
  • 迪米特原则:又称为最少知道原则,表示一个对象应该对其它对象保持最少的了解,只与直接的朋友通信
  • 合成复用原则 CRP:在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分; 新的对象通过向这些对象的委派达到复用已有功能的目的

工厂模式

以工厂的形式来生产对象的,不关注对象细节

function factory(name) {
            //创建对象
            var obj = {}
                // 对象赋属值
            obj.name = name
                // 返回
            return obj
        }
        let obj = factory("tom")
        console.log(obj);

单例模式(饿汉 、懒汉)

保证产生的对象只有一个 (不会被污染)

  • 利用闭包实现
//利用闭包实现
        function closeSingleton() {
            var single = null
            return function() {
                // 判断对应的对象是否为null
                // 如果为null则产生新对象
                if (!single) {
                    single = new Object()
                }
                return single
            }
        }
        var obj = closeSingleton()
        var single = obj()
        var single1 = obj()
        console.log(single == single1); // true
  • 利用原型实现
//原型实现:对象.prototype.方法
        function prototypeSingleton() {
            if (!prototypeSingleton.prototype.single) {
                // 在prototypeSingleton原型链上查找是否该方法,没有则创建
                prototypeSingleton.prototype.single = new Object()
            }
            return prototypeSingleton.prototype.single
        }
        var single = prototypeSingleton()
        var single1 = prototypeSingleton()
        console.log(single == single1);
  • 利用静态属性实现
//使用静态方法:对象.方法
        function staticSingleton() {
            if (!staticSingleton.single) {
                staticSingleton.single = new Object()
            }
            return staticSingleton.single
        }
        var single = staticSingleton()
        var single1 = staticSingleton()
        console.log(single == single1);
  • 使用global对象实现
//使用全局变量:this指向全局
        function globalSingleton() {
            if (!globalThis.single) {
                //globalThis表示global的指向,这里表示global指向window,也就是全局
                globalThis.single = new Object()
            }
            return globalThis.single
        }
        var single2 = globalSingleton()
        var single3 = globalSingleton()
        console.log(single2 == single3);

组合模式

将多个小结构组合成一个大结构 (将共有的函数放在一起调用)

function Fly() {
            this.init = () => {
                console.log('先飞呀');
            }
        }
        function Run() {
            this.init = () => {
                console.log('后跑呀');
            }
        }
        function Jump() {
            this.init = () => {
                console.log('再跳呀');
            }
        }
        //普通调用
        new Fly().init()
        new Run().init()
        new Jump().init()
        //封装一个函数来调用
        function Action() {
            this.obj = []
            //传入对应的函数对象
            this.add = function(...args) {
                this.obj = this.obj.concat([...args])
            }
            // 执行对应函数的init方法
            this.exec = function(fnName) {
                // 遍历对应的对象数组,调用里面的相关方法
                this.obj.forEach((args) => {
                    // 调用对应的方法
                    args[fnName].call(this)
                })
            }
        }
        let action = new Action()
        // 添加函数事件
        action.add(new Fly(), new Jump(), new Run())
        // 调用对应的方法
        action.exec('init')

组合模式的应用,模拟vue中的use和install

集中式架构技术有哪些 集中式设计_设计模式_02

class Vue {
            //解析对应的对象 执行对应的install
            static use(...objs) {
                objs = objs.map(v => {
                    //如果没有install方法
                    if (!v.install) {
                        if (typeof v != 'function') {
                            throw new Error('传入内容出错')
                        }
                        //将本身当作insatll函数
                        let fn = v
                        v = {
                            install() {
                                fn()
                            }
                        }
                    }
                    return v
                })
                Vue.exec(objs)
            }
            //传入对象进行执行
            static exec(arr) {
                arr.forEach(obj => {
                    obj['install'].call(this, Vue)
                });
            }
        }

        Vue.use({
            install() {
                console.log('吃饭了么');
            }
        }, {
            install() {
                console.log('你好啊');
            }
        })
        Vue.use({
            install(vue) {
                console.log('嘿嘿');
            }
        })

装饰器模式

用一个新的类将对应的原本的对象进行包装再进行加强(在不改变原有对象的基础上增强对象)

// 在不改变原有对象的基础上,增强对应的对象
        //原有的类
        function Person() {
            this.run = function() {
                console.log('跑');
            }
        }
        //增强 类
        function Stronger(person) {
            this.person = person
            this.run = function() {
                this.person.run()
                console.log("飞翔");
            }
        }
        let person = new Person()
        person.run() //原有类的调用
        let stronger = new Stronger(person)
        stronger.run() //增强类调用原有类

Ts内置有对应的装饰器 Decorator TS中使用注解来进行对应的装饰器添加 @decorator

代理模式

概述

代理模式是在原有对象的基础上利用代理对象来增强对应的对象 ,代理对象通常访问的是实际的对象。

示例

我(原本的对象)请了会计(代理),会计给我管钱 (功能增强),当会计(代理)把钱花完了(我(原本对象)的钱也没有)

  • 代理对象对原本的对象进行了功能增强
  • 代理对象影响的是实际的对象
  • ES7新增对应的Proxy的类来帮助我们进行代理

Proxy (vue3的底层实现)

实例化 (传入对应的被代理对象,处理对象,产生一个代理对象)

var proxy = new Proxy ( target , handler )

// 在不改变原有对象的基础上,增强对应的对象,代理对象通常访问的是实际的对象
        // 使用es7新增的proxy类来实现
        var obj = { //obj是被代理对象
            name: 'tom',
            age: 20
        }
        var proxy = new Proxy(obj, {
            //获取相关属性:被代理对象、属性名、代理对象
            get(targetObj, attributeName, proxyObj) {
                // console.log(arguments);
                if (attributeName == 'name') {
                    return '姓名为' + targetObj[attributeName]
                } else if (attributeName == 'age') {
                    return targetObj[attributeName] + '岁'
                }
                return targetObj[attributeName]
            },
            //设置相关属性:被代理对象、属性名、属性值、代理对象
            set(targetObj, attributeName, attributeValue, proxyObj) {
                targetObj[attributeName] = attributeValue
            },
            //定义属性和属性值
            // defineProperty(targetObj, attributeName, attributeValue, proxyObj) {
            //     Object.defineProperty(proxyObj, 'sex', {
            //         configurable: true, //是否可以删除
            //         enumerable: true, //是否可以遍历
            //         value: '男', //属性值
            //         writable: true //是否可以改变
            //     })
            // },
            //删除属性和属性值
            deleteProperty(targetObj, attributeName, proxyObj) {
                delete targetObj[attributeName]
            },
            //目标对象 对应的属性 默认返回false 使用in关键词调用
            has(targetObj,p){
                console.log(targetObj,p)
                return p in targetObj
            },
        })
        console.log(proxy.age); //   20   调用get
        console.log(proxy.name); //  tom  调用get
        proxy.name = 'marry' //设置name
        proxy.age = 10 //设置
        console.log(proxy); //  Proxy {name: 'marry', age: 10} 调用get
        console.log(obj); //{name: 'marry', age: 10} 原来的被代理对象也发生改变
        console.log('name' in proxy) // true  调用has
        delete proxy.name // 删除了obj中的name
        console.log(obj); //{age: 10}

proxy的handler相关属性方法

  • get ( 被代理对象、属性名、代理对象 ) ——访问属性的时候调用
  • set ( 被代理对象、属性名、代理对象 ) ——设置属性的时候调用
  • defineProperty ( 被代理对象、属性名、属性值、代理对象) ——定义属性的时候调用
  • deleteProperty ( 被代理对象、属性名、代理对象) ——删除属性的时候调用
  • has ( 被代理对象、属性名) ——判断是否存在,返回一个布尔值。

集中式架构技术有哪些 集中式设计_Vue_03

观察者模式(obServer、发布者-订阅者模式)

观察者模式是前端最常用的模式,它相当于对应的监听和处理执行的机制。

观察者模式的核心内容

  • 事件发布者:发布者
  • 事件监听者:订阅者
  • 事件处理:相关处理

【注意】

  • 事件名和处理函数的关系

一对多 :一个事件可以有多个处理函数、click [ handler1 , handler2 ]

  • 发布者和事件名的关系

一对多 :一个发布者可以发布多个事件、 { 事件名 : [ 处理函数 ] }

常见的发布订阅者模式 —— addEventListener (事件监听器)

//element.addEventListener(事件名,handler)
box.addEventListener('click',function(){
    //处理
    console.log('处理了')
})

模仿eventListener来实现发布订阅者模式

class Observer {
            constructor() {
                //存储事件和处理函数的容器{click:[handler],mosuedown:[handler]}
                this.events = {}
            }
            //添加监听事件
            on(eventName, handler) {
                    //判断是否存储了对应的事件
                    if (eventName in this.events) {
                        //如果存储了就给他加到对应的处理函数数组内
                        this.events[eventName].add(handler)
                    } else {
                        //如果没有的话先需要开劈一个数组 将处理函数装入
                        this.events[eventName] = new Set([handler])
                    }
                }
                //执行事件
            emit(eventName, ...args) {
                // 判断该事件是否存在
                if (!this.events[eventName]) {
                    return
                }
                //遍历集合,执行处理函数
                this.events[eventName].forEach(handler => {
                    //调用对应的函数传递参数
                    handler.apply(this, args)
                });
            }
            //删除监听事件
            off(eventName, handler) {
                //查询是否具备对应的事件
                //不具备结束对应的事件
                if (!this.events[eventName]) {
                    return
                }
                //具备的话 删除对应的事件
                this.events[eventName].delete(handler)
            }
        }
        let observer = new Observer()
        observer.on('click', function() { //添加监听事件并直接调用
            console.log('点击了')
        })
        observer.off('click', function() { //无法删除
            console.log('点击了')
        })
        observer.on('click', handler) //添加监听事件
            // observer.off('click', handler) //可以删除 

        function handler(arg) {
            console.log('点击了1' + arg)
        }
        observer.emit('click', '哈哈哈') //调用执行方法

【总结】

  • off 是用于取消事件
  • on 是用于监听事件
  • emit 是用于执行事件,传入的参数可以被对应的 on 接收
  • off 调用一定要 emit 之前
  • 存储使用的对象进行存储(key:事件名,value:处理函数集合)
  • 观察者模式是 vue2 底层实现之一 (对应的数据双向绑定必须使用观察者模式)