设计模式 | 青训营笔记

48 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

23种设计模式

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

浏览器中的设计模式

单例模式

定义:全局唯一访问对象

应用:缓存,全局状态管理

例子:浏览器中的window

//用单例模式实现请求缓存
//类实现
import api from "./utils";
export class Requset{
    static instance:Requset;
    private cache:Record<string,string>;
    constructor(){
        this.cache {}
    }
    static getInstance(){
        if (this.instance){
            return this.instance;
        }
        this.instance new Requset();
        return this.instance;
    }
    public async request(url:string){
        if (this.cache[url]){
            return this.cache[url];
        }
        const response = await api(url);
        this.cache[url] = response;
        return response;
    }
}
​
//函数式实现
import api from "./utils";
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;
};

发布订阅模式

定义:一种订阅机制,可在被订阅对象发生变化时通知订阅者

应用场景:从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅,上线订阅等,应用广泛。

如:

const button document.getElementById("button");
const dosomthing1 = () =>console.log("Send message to user");
const doSomthing2 = ()=>console.log("Log...");
button.addEventListener("click",doSomthing1);
button.addEventListener("click",doSomthing2);
//用发布订阅模式实现用户上线订阅
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){
        user.followers.push({user,notify })
    }
    online(){
        this.status = "online";
        this.followers.forEach(({notify })=>{
            notify(this);
        });
    }
}
//测试
test("should notify followers when user is online for multiple users",()=>(
    const user1 = new User("user1");
    const user2 = new User("user2");
    const user3 = new User("user3");
​
    const mockNotifyUser1 = jest.fn();
    const mockNotifyUser2 = jest.fn();
​
    user1.subscribe(user3,mockNotifyUser1);
    user2.subscribe(user3,mockNotifyUser2);
​
    user3.online();
​
    expect(mockNotifyUser1).toBeCalledWith(user3);
    expect(mockNotifyUser2).toBeCalledWith(user3);
});

Javascript中的设计模式

原型模式

定义:复制已有对象来创建新的对象

应用场景:JS中对象创建的基本模式

//用原型模式创建上线订阅中的用户
const baseUser:User = {
    name:'',
    status:"offline",
    followers:[],
    
    subscribe(user,notify){
        user.followers.push({user,notify });
    },
    online(){
        this.status = "online";
        
        this.followers.forEach(({notify})=>{
            notify(this);
        });
    }
};
    export const createUser = (name:string)=>{
    const user:User = Object.create(baseUser);
    user.name = name;
    user.followers = [];
    return user;
};
//测试
test("should notify followers when user is online for multiple users",()=>(
    const user1 = createUser("user1");
    const user2 = createUser("user2");
    const user3 = createUser("user3");
​
    const mockNotifyUser1 = jest.fn();
    const mockNotifyUser2 = jest.fn();
​
    user1.subscribe(user3,mockNotifyUser1);
    user2.subscribe(user3,mockNotifyUser2);
​
    user3.online();
​
    expect(mockNotifyUser1).toBeCalledWith(user3);
    expect(mockNotifyUser2).toBeCalledWith(user3);
});

代理模式

定义:可自定义控制对原对象的访问方式,并且允许在更新前后作一些额外处理

应用场景:监控,代理工具,前端框架实现等

//使用代理模式实现用户状态订阅
export const createProxyUser(ndme:string)=>{
    const user = new User(name);
    
    const proxyUser new Proxy(user,{
        set:(target, prop:keyof User, value)=>{ //设置对user的set拦截器
            target[prop] = value;
            if (prop === "status"){
                notifyStatusHandlers(target,value);
            }
            return true;
        },
    });
    const notifystatusHandlers(user:User,status:"online" | "offline")=>{
        if (status === "online"){
            user.followers.forEach(({notify})=
                notify(user);
            });
        }
    };
    
    return proxyUser;
};

迭代器模式

定义:在不暴露数据类型的情况下访问集合中的数据

应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口

//例子
const numbers = [1,2,3];
const map = new Map();
map.set("k1","v1");
map.set("k2","v2");
const set = new Set(["1","2","3"]);
for (const number of numbers){
    //...
}
for (const [key,value]of map){
    //...
}
for (const key of set){
    //...
}
//用for...of迭代所有组件
class MyDomElement{
    tag:string;
    children:MyDomElement[];
    constructor(tag:string){
        this.tag = tag;
        this.children = [];
    }
    addchildren(component:MyDomELement){
        this.children.push(component);
    }
    [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 }
            }
        };
    }
}

前端框架中的设计模式

代理模式

Vue当中对DOM操作的代理

原本:更改DOM属性 → 试图更新

vue: 更改DOM属性 → 更新虚拟DOM → 视图更新 (包括Diff算法)

具体实现见Vue源码

组合模式

定义:可多个对象组合使用,也可单个对象 独立使用

应用场景:DOM,前端文件,文件目录,部门

例子是React的组件结构

练习题

使用组件模式实现一个文件夹结构

  • 每个文件夹可以包含文件和文件夹
  • 文件有大小
  • 可获取每个文件夹下文件的整体大小