前端设计模式及应用 | 青训营笔记

115 阅读4分钟

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

前言

总结一下前端的设计模式

设计模式简介

在软件工程中,设计模式(Design Pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。

根据模式的目的来划分的话,GoF(Gang of Four)设计模式可以分为以下 3 种类型:

1、创建型模式:用来描述 “如何创建对象”,它的主要特点是 “将对象的创建和使用分离”。包括单例、原型、工厂方法、抽象工厂和建造者 5 种模式。

2、结构型模式:用来描述如何将类或对象按照某种布局组成更大的结构。包括代理、适配器、桥接、装饰、外观、享元和组合 7 种模式。

3、行为型模式:用来识别对象之间的常用交流模式以及如何分配职责。包括模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录和解释器 11 种模式。

单例模式

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

单例就是保证一个类只有一个实例,实现方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

用单例模式实现请求缓存

import {api} from "./utils";
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();
    }
    
    public async request(url: string) {
        if (this.cache[url]){
            return this.cache[url];
        }
        const response = await api(url);
        this.cache[url] = response;
        return response;
    }
}

观察者模式|发布订阅模式

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

用观察者模式实现用户上线订阅

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);
        });
    }
}

原型模式

定义:复制已有对象来创建新的对象 应用场景: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 = Obeject.create(baseUser);
    user.name = name;
    user.followers = [];
    return user;
}

代理模式

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

使用代理模式实现用户状态订阅

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==="oneline") {
            user.followers.forEach(({notify})=>{notify(user});
        }
    }
}

迭代器模式

定义:在不暴露数据类型的情况下访问集合中的数据 应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口。

迭代器 : dom子节点迭代

[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};
        }
    }
}

组合模式

组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

实现文件夹管理

// 定义接口规范
interface Compose {
    name: string,
    add(file: CFile): void,
    scan(): void
}
// 树对象 - 文件目录
class CFolder implements Compose {
    fileList = [];
    name: string;
    constructor(name: string) {
    this.name = name;
}
    add(file: CFile) {
        this.fileList.push(file);
    }
    scan() {
        for (let file of this.fileList) {
            file.scan();
        }
    }
}
// 叶对象 - 文件
class CFile implements Compose {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    add(file: CFile) {
        throw new Error('文件下面不能再添加文件');
    }
    scan() {
        console.log(`开始扫描:${this.name}`)
    }
}
let mediaFolder = new CFolder('娱乐');
let movieFolder = new CFolder('电影');
let musicFolder = new CFolder('音乐');
let file1 = new CFile('钢铁侠.mp4');
let file2 = new CFile('再谈记忆.mp3');
movieFolder.add(file1);
musicFolder.add(file2);
mediaFolder.add(movieFolder);
mediaFolder.add(musicFolder);
mediaFolder.scan();

工厂模式

工厂模式是一种创建模式,用于抽象创建特定对象的过程。

简单工厂模式创建对象

const Person = (name, age) => {
  return {
    name,
    age,
    birthday() {
      console.log(`${name}长大了一岁`);
      this.age++;
    },
    eat() {
      console.log(`${name}正在吃饭`);
    },
  };
};

//创建了两个人
const person1 = Person("Jack", 32);
const person2 = Person("Lisa", 5);

console.log(person1.name); //"Jack"
console.log(person2.age); //"5"
person2.birthday(); //"Lisa 长大了一岁"
console.log(person2.age); //"6"

继承的实现:比如我们希望再封装一个学生对象,学生对象应当继承于人

const Student = (name, age, job) => {
  const prototype = Person(name, age); //继承 Person 对象
  return Object.assign({}, prototype, {
    job,
    study() {
      console.log(`${name}正在学习`);
    },
  });
};

//创建一个学生
const student1 = Student("Jennie", 18, "student");
console.log(student1.name); //"Jennie"
console.log(student1.age); //"18"
student1.birthday(); //"Jennie 长大了一岁"
student1.study(); //"Jennie 正在学习"

原型模式、工厂模式、构造函数模式三者区别?

责任链模式

责任链接模式又称职责链模式,是一种对象的行为模式;它是一种链式结构,每个节点都有可能两种操作,要么处理该请求停止该请求操作,要么把请求转发到下一个节点,让下一个节点来处理请求;该模式定义了一些可能的处理请求的节点对象,请求的起点跟顺序都可能不一样,处理的节点根据请求的不一样而不同;请求者不必知道数据处理完成是由谁来操作的,内部是一个黑箱的操作过程,这是它的一个核心内容;

function Handler() {
    this.next = null;
    this.setNext = function(_handler) {
        this.next = _handler;
    };

    this.handleRequest = function(money) {

    }
};

//采购部
function CGBHandler = function() {}
CGBHandler.prototype = new Handler();
CGBHandler.prototype.handleRequest = function(money) {
     //处理权限最多10000
    if (money < 10000){
        console.log('同意');
    } else {
        console.log('金额太大,只能处理一万以内的采购');
        if (this.next) {
            this.next.handleRequest(money);
        }
    }
};

//总经理
function ZJLHandler = function() {}
ZJLHandler .prototype = new Handler();
ZJLHandler .prototype.handleRequest = function(money) {
     //处理权限最多100000
    if (money < 100000){
        console.log('10万以内的同意');
    } else {
        console.log('金额太大,只能处理十万以内的采购');
        if (this.next) {
            this.next.handleRequest(money);
        }
    }
};

//董事长
function DSZHandler = function() {}
DSZHandler .prototype = new Handler();
DSZHandler .prototype.handleRequest = function(money) {
     //处理权限至少100000
    if (money >= 100000){
        console.log('10万以上的我来处理');
        //处理其他逻辑
    } 
};

总结

设计模式在开发中是比较重要的开发思想,我们在前端开发的时候应该主要以这些思想去考虑如何去设计我们的代码。