青训营笔记:设计模式篇

75 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的的第3天 主要内容为设计模式

1.设计模式的定义

设计模式是软件设计中常见问题的解决方案模型

  • 是历史经验的总结
  • 与特定语言无关

2.设计模式分类

一共有23中设计模式

  • 创建型-如何创建一个对象
  • 结构型-如何灵活的将对象组装成较大的结构
  • 行为型-负责对象间的高效通信和职责划分

3.浏览器中的设计模式

1.单例模式

  • 定义:全局唯一访问对象
  • 应用场景:应用于缓存,全局状态管理等。

image.png

用单例模式实现请求缓存

写法1

import {api} from './utils'
//1.传统的类的写法
export class Request{
    static instance:Request;
    private cache:Record<string,string>;
    constructor()
    {
        this.cache={}
    }

    static getInstance()
    {
        if(this.instance)
        {
            return this.instance
        }
        this.instance=new Request();
        return this.instance;
    }
    public async request(url:string)
    {
        if(this.cache[url])return this.cache[url];
        const response=api(url);
        this.cache[url]=response;
    }
}

写法2

const cache:Record<string,string>={}
export const request=async(url:string)=>{
    if(cache[url]){
        return cache[url]
    }
    const response=await Api(url)
    cache[url]=response;
    return response;
}

写法2与写法1相比没有采用传统的类的方式来实现

2.发布订阅模式

  • 定义:一种订阅机制,可在被订阅对象发生变化时同之订阅者。
  • 应用场景:从系统架构之间解耦,到业务中一些实现模式,像邮件订阅,上线订阅等,应用广泛。

用发布订阅实现用户上线订阅

type Notify=(user:User)=>void;

export class User{
    name:String;
    status:"offline"|"online";
    followers:{user:User;notify:Notify}[];
    constructor(name:String)
    {
        this.name=name;
        this.status="offline";
        this.followers=[]
    }
    subscribe(user:User,notify:Notify)
    {
        this.followers.push({user,notify})
    }
    online()
    {
        this.status='online';
        this.followers.forEach(item=>item.notify(item.user));
    }
}

4.js中的设计模式

1.原型模式

  • 定义:复制已有对象来创建新的对象
  • 应用场景:js中对象创建的基本模式

用原型模式创建上线订阅中的用户

type Notify=(user:User)=>void;

export class User{
    name:String;
    status:"offline"|"online";
    followers:{user:User;notify:Notify}[];
    constructor(name:String)
    {
        this.name=name;
        this.status="offline";
        this.followers=[]
    }
    subscribe(user:User,notify:Notify)
    {
        this.followers.push({user,notify})
    }
    online()
    {
        this.status='online';
        this.followers.forEach(item=>item.notify(item.user));
    }
}
const baseUser:User={
    name:"",
    status:'offline',
    followers:[],
    subscribe(user,notify)
{
    this.followers.push({user,notify});
},
online()
{
    this.status='online';
    this.followers.forEach(item=>{
            item.notify(this)
    })
}
}

export const createUser=(name:string)=>{
    const User:User=Object.create(baseUser)
    User.name=name;
    User.followers=[];
    return User;
}

2.代理模式

  • 定义:可自定义控制对原型对象的访问方式,并且允许在更新前后做一些额外处理
  • 应用场景:监控,代理工具,前端框架实现等。

使用代理模式实现用户的发布订阅

//实现基本的user类

type Notify=(user:User)=>void;
class User{
    name:string;
    status:"offline"|"online";
    followers:{user:User,notify:Notify}[];
    constructor(name:string)
    {   
        this.name=name;
        this.status='offline';
        this.followers=[];
    }
    subscribe(user:User,notify:Notify)
    {
        this.followers.push({user,notify});
    }
    online()
    {
        this.status="online"
    }
}
export const createProxyUser=(name:string)=>{

    const user=new User(name)
    const proxyUser=new Proxy(user,{
        set:(target,prop:keyof User,value)=>{
            target[prop]=value;
            if(prop==='status')
            {
                notifyStatusHandlers(target,value);
            }
            return true
        }
    })
    const notifyStatusHandlers=(user:User,status:"online"|"offline")=>{
            if(status==='online')
            {
                user.followers.forEach((item)=>{
                    item.notify(user)
                })
            }
    }
    return proxyUser
}

让online方法只更改state,后续其他的操作交给notifyStatusHandlers来处理,使用createProxyUser来创建user

3.迭代器模式

  • 定义:在不暴露数据类型的情况下访问集合中的数据
  • 应用场景:用来遍历数据结构入列表,树等。

用for of迭代自己创建的类树组件

class MyDomElement{
    tag:string;
    children:MyDomElement[];
    constructor(tag:string)
    {
        this.tag=tag;
        this.children=[];
    }
    addChildren(child:MyDomElement)
    {
        this.children.push(child)
    }
    [Symbol.iterator](){
        const list=[...this.children]
        let node;
        return {
            next:()=>{
                while((node=list.shift())){
                    node.children.length>0&&list.push(...node.children);
                    return {value:node,done:false};
                }
                return {value:null,done:true};
            }
        }
    }
}

5.前端框架中的设计模式

1.代理模式

用Vue实现计数器

image.png

传统的方法中 我们是监听click事件,在click事件中手动的通过innerHTML的方法将变化后的count值来更新视图。 而依靠代理模式 Vue将我们更改DOM属性的操作更新到了虚拟DOM上,然后通过diff算法来更新视图。并不是直接更新视图,而是代理到了虚拟DOM上。

image.png

2.组合模式

  • 可以多个对象组合使用,也可以单个对象独立使用
  • 应用场景:DOM,前端组件,文件目录,部门

image.png

总结

  1. 总结出抽象的模式相对比较简单,但是想要将抽象的模式套用到场景中却非常困难
  2. 现代编程语言的多编程范式带来更多的可能性
  3. 真正优秀的开源项目学习设计模式并不断实践