设计模式详解

概述:

设计模式是一种固定的解决某个问题的一种方式,他是一个固定的模式(原理都是一样的),他不区分语言。常用的设计模式有23种,他分为三类(主要针对的是类和对象)。

设计模式分类

  • 创建型 (创建对象)单例模式、工厂模式

  • 结构型(将多个小结构变成一个大结构)组合模式 代理模式 装饰器模式 适配器模式

  • 行为型 (对应类和对象的行为进行相关处理)观察者模式

设计模式的原则

  • 开闭原则

  • 里式置换原则

  • 单一原则

  • 依赖倒置原则

  • 接口隔离原则

  • 迪米特原则

工厂模式讲解

  • 根据对应的工厂方法生成对应的对象(生产相关对象)

function factory(name){
    //手动构建对象
    var obj = new Object()
    //给对象进行属性赋值
    obj.name = name
    //手动返回对象
    return obj
}
//调用工厂生产对象
let obj1 = factory('jack')
let obj2 = factory('tom')

单例模式

  • 保证生成的对象实例只有一个

基础类

//基础类
class Person{
    constructor(){
        
    }
}

闭包实现

//闭包的单例模式
function closureSingleton(){
    //接收返回对象的变量
    var instance = null
    return function(){
        //如果当前的变量值为null
        if(!instance){
            //构建一个对象赋值给返回的变量
            instance = new Person()
        }
        return instance
    }
}
let single = closureSingleton()
let person1 = single()
let person2 = single()
console.log(person1==person2)

原型

//原型的单例
function prototypeSingleton(){
    //判断原型上是否具备newPerson这个属性
    if(!prototypeSingleton.prototype.instance){
        prototypeSingleton.prototype.instance = new Person()
    }
    return prototypeSingleton.prototype.instance
}
let person3 = prototypeSingleton()
let person4 = prototypeSingleton()
console.log(person3 == person4)

静态属性

//静态属性单例
function staticSingleton(){
    if(!staticSingleton.instance){
        staticSingleton.instance = new Person()
    }
    return staticSingleton.instance
}
let person5 = staticSingleton()
let person6 = staticSingleton()
console.log(person5 == person6)

全局属性实现

  • window是浏览器global对象 但是其他的global对象不是window

//全局对象实现
function globalSingleton(){
    if(!window.instance){
        window.instance = new Person()
    }
    return window.instance
}
let person7 = globalSingleton()
let person8 = globalSingleton()
console.log(person7 == person8)

组合模式 (*)

  • 将多个对象的相同方法或者属性组合到一块(将不同的方法组合在一块执行)

基础内容

class GoToHome{
    constructor(){
        this.action = ()=>{
            console.log('回家')
        }
    }
}
class OpenComputer{
    constructor(){
        this.action = ()=>{
            console.log('打开电脑')
        }
    }
}
class PlayGame{
    constructor(){
        this.action = ()=>{
            console.log('玩游戏')
        }
    }
}
new GoToHome().action()
new OpenComputer().action()
new PlayGame().action()

将多个的方法组合在一块进行统一调用

class Combiner{
    constructor(){
        this.list = []
    }
    //将对应的对象加入到list中
    push(obj){
        this.list.push(obj)
    }
    //统一执行相关函数
    execute(fnName,...arg){
        //遍历list
        this.list.forEach(item=>{
            //执行对应的函数
            item[fnName].call(this,...arg)
        })
    }
}
let combiner = new Combiner()
//将对象加入
combiner.push(new GoToHome())
combiner.push(new OpenComputer())
combiner.push(new PlayGame())
//执行action方法
combiner.execute('action')

组合模式在vue中的使用

  • Vue.use 方法(自动执行install)

  • install 方法

  • 如果在use里面传入的是一个函数 那么他会把这个函数直接当成install 如果传的是对象 他会自动去找里面的instal方法

模拟实现use和install

class Vue {
    constructor() {
        this.fnList = []
    }
    use(obj) {
        //如果他是一个函数 那么这个函数直接当成install
        if (typeof obj == 'function') {
            this.fnList.push(obj)
        }
        //如果他是对象就找里面的install
        if (typeof obj == 'object') {
            this.fnList.push(obj.install)
        }
        //调用执行
        this.execute()
    }
    execute() {
        //遍历对应的函数列表
        this.fnList.forEach((fn) => {
            fn()
        })
    }
}
var vue = new Vue()
vue.use({
    install() {
        console.log('你好')
    }
})
vue.use({
    install() {
        console.log('世界')
    }
})
vue.use(() => {
    console.log('哈哈哈哈')
})

装饰器模式

场景

我现在有三个车的类,这个三个车他都有对应的跑的方法,现在我需要给这个三个车加一个飞的方法(设计模式开闭原则,一个类一旦写完了不允许内部再进行修改),这个时候就可以使用对应的装饰器模式。

概述

装饰器模式是在不影响原本类的基础上,进行功能扩展。他又叫做包装模式。

实现

基础类

class Car{
    constructor(){
    }
    run(){
        console.log('跑')
    }
}

包装类

class Decorator{
    //将基础类传入包装类
    constructor(car){
        this.car = car
    }
    fly(){
        console.log('飞')
        this.car.run()
    }
}
new Decorator(new Car()).fly()

在typeScript中有个对应的装饰器修饰 @Decorator

观察者模式 (*)

概述:

观察者模式是前端最常用的模式,他又被称为发布者-订阅者模式。他的核心是一个对应的发布者进行发布,以及对应的有个订阅者(对于发布的内容进行监听),发布者将对应的内容发布,订阅者就会收到信息从而进行对应的处理。

观察者模式的核心内容

  • 发布者

  • 订阅者

  • 相关处理

其实我们的事件就是一个观察者模式(事件发布者 事件监听者 事件处理)

element.addEventListener('事件名',处理函数)
element.removeEventListener('事件名',处理函数)

简单的事件示例

<button>点我</button>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click',handler)
function handler(){
    console.log('点击了')
}
</script>
  • 发布者 button

  • 订阅者 JavaScript引擎

  • 处理 handler

分析这个事件的相关内容

  • 发布者

  • 事件名

  • 处理函数

相关关系

  • 事件名和处理函数的关系 一对多 (一个事件可以有多个处理函数)click:[handler1,handler2]

  • 发布者和事件名的关系 一对多 (一个发布者可以发布多个事件) {事件名:[处理函数]}

根据对应的事件监听机制来模拟观察者

事件监听的过程

  • 事件发布 (有对应的事件名)on

  • 事件执行 (根据对应的事件名执行相关的处理函数) emit

  • 事件取消 (将对应的事件移除)off

代码构建

class obServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
    
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}

代码完善

on方法

class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
    
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}

emit方法

class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //获取这个事件里面所有的处理函数 执行这些处理函数
        //遍历对应的处理函数数组
        this.obj[eventName].forEach(handler=>{
            //执行对应的处理函数 传入参数
            handler.call(this,...arg)
        })
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        
    }
}

off方法

class ObServer{
    constructor(){
        //{click:[handler1,handler2],mousemove:[handler1,handler2]}
        this.obj = {}
    }
    //事件发布 事件名  处理函数
    on(eventName,handler){
        //判断这个eventName是否存在
        //如果当前的事件名不存在 给他赋值一个空数组
        if(!this.obj[eventName]){
            this.obj[eventName] = []
        }
        //将事件添加进去
        this.obj[eventName].push(handler)
    }
    //事件执行 事件名  参数
    emit(eventName,...arg){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //获取这个事件里面所有的处理函数 执行这些处理函数
        //遍历对应的处理函数数组
        this.obj[eventName].forEach(handler=>{
            //执行对应的处理函数 传入参数
            handler.call(this,...arg)
        })
    }
    //事件取消 事件名  处理函数
    off(eventName,handler){
        //判断这个eventName是否存在
        if(!this.obj[eventName]) return
        //遍历对应的eventName里面处理函数数组 找到匹配的handler将他删除
        this.obj[eventName].forEach((v,i)=>{
            if(Object.is(v,handler)){
                this.obj[eventName].splice(i,1)
            }
        })
    }
}

注意事项

  • emit执行他可以传参传给对应的on方法里面处理函数(vue中父传子的实现及bus传值的实现)

  • off调用一定要emit之前

  • 观察者模式是vue2底层实现

代理模式(*)

概述:

在不改变原本的类的基础上,对于对象进行功能加强,代理模式代理出来的是对象。(代理模式低耦合)

示例

  • 你家里需要装修 但是你不想动手 这个时候你就会找一个装修队(代替你进行装修)

  • 代理对象和被代理对象是俩个不同的对象,但是实际操作的内容是一个

核心关键词

  • 被代理对象 你

  • 代理对象 装修对象

  • 操作内容 你家的房子

代理的实现

  • 在js中 es7新增了一个Proxy的类 这个类专门是用来做代理的 所以我们只需要掌握这个类的使用就ok了

Proxy的使用

新建代理对象(通过proxy的构造)

let proxy = new Proxy(被代理对象,处理对象)

基础使用

//被代理对象
let obj = {
    name: 'jack',
    age: 18
}
//通过proxy来新建代理对象 
//Proxy的构造函数需要传入被代理对象 以及相关的处理对象
//对于handler他是对于所有的属性进行操作
//get在获取属性的时候调用  他返回的结果就是你接收的结果
//set对于设置属性的时候调用
let proxy = new Proxy(obj,{
    //目标对象(被代理对象) 属性(实际访问的属性)  代理对象(当前proxy)
    get(target,attribute,proxyObj){ //这个里面返回的值就是当前的获取值
        console.log('调用了get');
        if(attribute == 'name'){
            return '姓名为'+target[attribute] 
        }else if(attribute == 'age'){
            return target[attribute]+'岁'
        }
        return target[attribute] 
    },
    //目标对象(被代理对象) 属性(实际访问的属性)值 代理对象(当前proxy)
    set(target,attribute,value,proxyObj){
        console.log('调用了set');
        target[attribute] = value
    },
    //定义属性 赋值新的属性  目标对象  属性名  属性的详情信息
    defineProperty(target,attribute,descriptor){
        console.log('新的属性定义');
    },
    //删除属性 delete的时候
    deleteProperty(target,attribute,proxyObj){
        console.log('删除属性');
        delete target[attribute]
    }
})

proxy的处理对象的四大属性

  • get 访问属性的时候调用

    console.log( proxy.name); //调用get
    console.log( proxy.age); //调用get
  • set 设置属性的时候调用

    proxy.name = '你好' //调用set
  • defineProperty 定义属性的时候调用

    Object.defineProperty(proxy,'sex',{
        configurable:true,//是否可以删除
        enumerable:true,//是否可以遍历
        value:'男',//属性值
        writable:true//是否可以改变
    })
  • deleteProperty 删除属性的时候调用

    delete proxy.name //调用deleteProperty

总结

  • Proxy是一个es7新增的一个类 他返回的是一个对象

  • Proxy里面传入被代理对象和对应的处理对象

  • 处理对象包含4个方法(get set defineProperty deleteProperty)

  • Proxy里面实际操作是被代理对象 (如果在里面操作代理对象会造成栈溢出)

  • 代理对象和被代理对象不是一个对象 但是操作的内容是一个都是被代理对象

  • Proxy是vue3的底层实现之一

适配器模式

概述

将旧的的内容进行新的适配,在原本的基础上做兼容处理。

常用的业务场景

  • 旧的接口替换新的接口(在不改变原本内容的情况下完成替换)

  • 表单验证 (根据场景切换相关的验证)

代码实现

class phone{
    constructor(){
        
    }
    fn(){
        return 22
    }
}
class Adaptive{
    constructor(){
        
    }
    fn(){
        return '220转为'+new Phone().fn()
    }
}
//实际使用的是对于的适配的内容
new Adaptive().fn()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值