《记一忘三二》设计模式笔记

32 阅读4分钟

创建型模式

构造器模式

对象的创建过程

function createPerson(name: string, age: number) {
  const person = Object.create(null);
  person.name = name;
  person.age = age;

  person.getName = function () {
    return this.name;
  };

  person.getAge = function () {
    return this.age;
  };

  return person;
}

const person1 = createPerson("李白", 18);
const person2 = createPerson("杜甫", 20);

不讲究属性和方法复用,比如person1person2在内层中都是占用独立的空间

需注意,要使用Object.create(null)创建一个独立的空白对象,让每次调用createPerson函数都是生成的对象都是内存中独立的空间

单纯的构造器模式在开发中很少使用,

原型模式

同构造器模式功能一样,同样是对象的创建过程

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  getName(): string {
    return this.name;
  }

  getAge(): number {
    return this.age;
  }
}

const person1 = new Person("李白", 18);
const person2 = new Person("杜甫", 20);

讲究方法复用,比如person1person2getName()getAge() 在内存中占用的时同一个空间

一般在函数原型链继承class语法 中使用,通过原型可复用的特点,节省内存

工厂模式

创建某一类对象

enum Role {
    ADMIN = 'admin',
    AGENT = 'agent'
}

type Permission = Array<'read-new' | 'write-new' | 'delete-new'>


class User {
    public role: Role
    public permission: Permission
    constructor(role: Role, permission: Permission) {
        this.role = role
        this.permission = permission
    }
}

function userFactory(role: Role) {
    if (role === Role.ADMIN) {
        return new User(role, ['read-new', 'write-new', 'delete-new'])
    }
    if (role === Role.AGENT) {
        return new User(role, ['read-new'])
    }
}

const user = userFactory(Role.AGENT)

userFactory函数就是User类的创建工厂

这里还可以进一步优化,当前User类在除userFactory函数以外的地方也能直接实例化,这样让User类实例化存在不可预知性

class User {
    public role: Role
    public permission: Permission
    private constructor(role: Role, permission: Permission) {
        this.role = role
        this.permission = permission
    }

    static userFactory(role: Role) {
        if (role === Role.ADMIN) {
            return new User(role, ['read-new', 'write-new', 'delete-new'])
        }
        if (role === Role.AGENT) {
            return new User(role, ['read-new'])
        }
    }
}

const user = User.userFactory(Role.AGENT)

User类 的创建变成了私有化,也就只能通过userFactory工厂函数创建了

抽象工厂模式

创建实现了同一个抽象父类的类

enum Role {
    ADMIN = 'admin',
    AGENT = 'agent'
}

abstract class User {
    public name: string
    public role: Role


    constructor(name: string, role: Role) {
        this.name = name
        this.role = role
    }

    abstract welcome(): void
}

class Admin extends User {
    constructor(name: string) {
        super(name, Role.ADMIN)
    }

    welcome() {
        console.log(`Hello ${this.name}管理员`);
    }

    addAgent() {
        // 添加代理
    }
}

class Agent extends User {
    constructor(name: string) {
        super(name, Role.AGENT)
    }

    welcome() {
        console.log(`Hello ${this.name}代理`);
    }
}

function userAbstractFactory(role: Role): typeof Admin | typeof Agent {
    if (role === Role.ADMIN) {
        return Admin
    }
    if (role === Role.AGENT) {
        return Agent
    }

    throw new Error('无对应的用户类型')
}

const ClassName = userAbstractFactory(Role.ADMIN)

const admin = new ClassName('李白')

工厂模式创建的对象因为属于一个类,所有属性和方法都是相同的;抽象工厂返回的类,每个类可以实现自己的不同的方法

简单来说,工厂模式是一个类根据不同情况创建一个对象;抽象工厂是在实现同一个抽象父类许多类中选择一个类

建造者模式

定义实体的表示过程

interface Task {
    init(): void
    run(): void
    stop(): void
}

class Convert implements Task {
    init() {
        console.log("转换任务初始化");
    }
    run() {
        return new Promise((resolve) => {
            setTimeout(() => {
                console.log("转换完成");
                resolve(undefined)
            }, 1000)
        })
    }
    stop() {
        console.log("结束转换");
    }
}


class Split implements Task {
    init() {
        console.log("分割任务初始化");
    }
    run() {
        return new Promise((resolve) => {
            setTimeout(() => {
                console.log("转换完成");

                resolve(undefined)
            }, 1500)
        })
    }
    stop() {
        console.log("结束分割");
    }
}

class TaskBuilder {
    private task: Task
    constructor(task: Task) {
        this.task = task
    }
    async build() {
        this.task.init()
        await this.task.run()
        this.task.stop()
    }
}

new TaskBuilder(new Convert()).build()
new TaskBuilder(new Split()).build()

工厂模式关心的是最后产生的什么实体,而建造者模式关心的是实体创建之后的表现过程

单例模式

限制一个类实例一个对象

class Model {
    private $el!: HTMLDivElement
    private constructor() {
        this.render()
    }

    private render() {
        const $el = document.createElement('div')
        $el.innerText = '升级提示'
        Object.assign($el.style, {
            width: '200px',
            height: '200px',
            background: '#ccc',
            display: 'none',
            justifyContent: 'center',
            alignItems: 'center',
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)'
        })
        document.body.appendChild($el)
        this.$el = $el
    }

    show() {
        this.$el.style.display = 'flex'
    }

    hide() {
        this.$el.style.display = 'none'
    }



    private static instance: Model
    public static createInstance() {
        if (!this.instance) {
            this.instance = new Model()
        }
        return this.instance
    }
}

const model = Model.createInstance()

把类的创建权利交给createInstance,限制只实例一个对象,可以看做一个特殊的工厂模式

结构型模式

装饰器模式

在不影响现有功能下,动态给实体添加功能

function pvLog(target: typeof Page, fieldName: keyof typeof Page) {
    const originalFun = Reflect.get(target, fieldName) as { (...arg: any[]): any }
    return {
        value(...arg: any[]) {
            console.log("pv统计");
            originalFun.apply(this, arg)
        }
    }
}

class Page {
    @pvLog
    static toLogin() {
        console.log("跳转到登录页面");
    }
}

Page.toLogin()

适配器模式

让原本因不兼容而无法正常完成的工作,变的兼容并且完成

class TencentMap {
    show() {
        console.log("显示腾讯地图");
    }
}

class BaiduMap {
    display() {
        console.log("显示百度地图");
    }
}


interface MapContext {
    render(): void
}

class TencentMapAdapter extends TencentMap implements MapContext {
    render() {
        this.show()
    }
}

class BaiduMapAdapter extends BaiduMap implements MapContext {
    render() {
        this.display()
    }
}


function render(map: MapContext) {
    map.render()
}

render(new TencentMapAdapter())
render(new BaiduMapAdapter())

也可以用在处理前后端数据格式不一致情况,为数据转换提供适配器

代理模式

截取对实体的操作,由代理去操作实体

interface Movie {
    name: string
    price: number
}

class Star {
    participateMovies: Array<Movie> = []

    addMovie(movie: Movie) {
        this.participateMovies.push(movie)
    }
}

class Agent {
    private star: Star = new Star()

    addMovie(movie: Movie) {
        if (movie.price < 100) {
            throw new Error("价格太低")
        }
        this.star.addMovie(movie)
    }
}


const agent = new Agent()
agent.addMovie({ name: "《星际穿越》", price: 1000 })

Proxy专门用于代理模式

装饰器模式、适配器模式、代理模式的区别

装饰器模式适配器模式代理模式
功能点缀原有功能转换原有功能和数据,让原有的功能正常运行代理原有功能和数据,给原有功能添加限制
是否影响原本功能不影响影响不影响
去除之后,原有功能是否能正常运行不能
和原有实体的关系可以多个实体使用可以用于多个实体一个代理对应一个实体

外观模式

为多个子系统提供操作调度的实体,负责连接多个子系统

class Input {
    render() {
        return `
            <div class="todo--header">
                <input class="todo--input" type="text" placeholder="请输入事项内容"/>
                <button class="todo--btn">添加</button>
            </div>
        `;
    }
}

class List {
    private data: Array<any>;

    constructor(data: Array<any>) {
        this.data = data;
    }

    render() {
        return `
            <ul class="todo--list">
                ${this.data
                .map(
                    (item) => `
                    <li class="todo--item">
                        <span class="todo--content">${item.content}</span>
                    </li>
                `
                )
                .join("")}`;
    }
}

class TodoList {
    private input: Input;
    private list: List;

    private data: Array<any> = [{
        content: "hello world",
    }];
    constructor() {
        this.input = new Input();
        this.list = new List(this.data);
    }

    render() {
        return `
            <div>
                ${this.input.render()}
                ${this.list.render()}
        </div>
            `;
    }
}

桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立变化

interface IAnimation {
    show(el: HTMLDivElement): void
    hide(el: HTMLDivElement): void
}

class Rotate implements IAnimation {
    show(el: HTMLDivElement): void {
        el.style.transform = 'rotate(360deg)'
    }

    hide(el: HTMLDivElement): void {
        el.style.transform = 'rotate(0deg)'
    }
}

class Scale implements IAnimation {
    show(el: HTMLDivElement): void {
        el.style.transform = 'scale(1)'
    }

    hide(el: HTMLDivElement): void {
        el.style.transform = 'scale(0)'
    }
}

class Toast {
    message: string
    animation: IAnimation
    el!: HTMLDivElement
    constructor(message: string, animation: IAnimation) {
        this.message = message
        this.animation = animation

        this.render()
    }

    render() {
        const div = document.createElement('div')
        Object.assign(div.style, {
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            padding: '10px 20px',
            background: '#333',
            color: '#fff',
            borderRadius: '4px',
            fontSize: '14px',
            zIndex: '9999',
            boxShadow: '0 0 3px rgba(0,0,0,.3)',
            transition: 'all .3s ease-in-out',
        })
        div.innerText = this.message
        document.body.appendChild(div)
        this.el = div
        setTimeout(() => {
            this.animation.show(this.el)

        })
    }

    destroy() {
        this.animation.hide(this.el)
        this.el.addEventListener('transitionend', () => {
            document.body.removeChild(this.el)
        })
    }
}


const toast = new Toast('Hello World', new Scale())

setTimeout(() => {
    toast.destroy()
}, 3000)

组合模式

处理树结构数据,以部分代表全部,操作部分也就操作的全部

只要一个类管理了一个数组,就可以称为组合模式

abstract class Context {
    protected name: string;

    constructor(name: string) {
        this.name = name;
    }

    abstract scan(): void
}


class File extends Context {
    constructor(name: string) {
        super(name);
    }

    scan() {
        console.log(`${this.name}文件扫描完毕`);
    }
}

class Directory extends Context {

    private children: Array<Context> = [];
    constructor(name: string) {
        super(name);
    }

    add(item: Context) {
        this.children.push(item);
    }

    scan() {
        console.log(`${this.name}文件夹扫描完毕`);
        this.children.forEach((item) => item.scan());
    }
}


let rootDirectory = new Directory('root');
let frontDirectory = new Directory('front');
let backDirectory = new Directory('back');

rootDirectory.add(frontDirectory);
const cssFile = new File('css');
const jsFile = new File('js');
frontDirectory.add(cssFile)
frontDirectory.add(jsFile)
rootDirectory.add(backDirectory);


rootDirectory.scan()

行为模式

观察者模式

实体一对多的依赖关系,以便当一个实体的状态发生改变时,所有依赖于它的实体都得到通知并自动更新

class Subject {
    private observers: Observer[] = []

    add(observer: Observer) {
        this.observers.push(observer)
    }

    notify() {
        this.observers.forEach(observer => observer.update())
    }

    remove(observer: Observer) {
        this.observers = this.observers.filter(item => item !== observer)
    }
}

class Observer {
    private name: string
    constructor(name: string) {
        this.name = name
    }
    update() {
        console.log(this.name + '触发update方法')
    }
}

const subject = new Subject()
const observer1 = new Observer("观察者1号")
const observer2 = new Observer("观察者2号")
subject.add(observer1)
subject.add(observer2)

setTimeout(() => {
    subject.notify()
}, 1000)

可以用于组件通信,或者数据回调

订阅发布模式

和观察者模式类似,但是订阅发布模式可以管理多个事件类型

class PubSub {
    private events: Map<string, Array<Function>> = new Map()

    on(eventName: string, callback: Function) {
        if (!this.events.get(eventName)) {
            this.events.set(eventName, [])
        }
        this.events.get(eventName)!.push(callback)
    }

    emit(eventName: string, ...args: any[]) {
        const callbacks = this.events.get(eventName)
        if (callbacks) {
            callbacks.forEach(callback => callback(...args))
        }
    }

    off(eventName: string, callback: Function) {
        const callbacks = this.events.get(eventName)
        if (callbacks) {
            this.events.set(eventName, callbacks.filter(cb => cb !== callback))
        }
    }
}

const pubSub = new PubSub()

pubSub.on('test', () => {
    console.log('test')
})

pubSub.on('test', () => {
    console.log('test2')
})

pubSub.emit('test')

观察者模式和订阅发布模式区别

观察者模式订阅发布模式
触发类型一种事件多种事件名称

模块模式

隔离私有和共有的属性和方法

let count = 0

const add = () => count++
const subtract = () => count--

export {
    add,
    subtract
}

命令模式

type CommandCode = 'log';


class Receiver {
    log() {
        console.log('Receiver 执行了');
    }
}

class Command {
    private receiver: Receiver;

    constructor(receiver: Receiver) {
        this.receiver = receiver;
    }

    execute(code: CommandCode) {
        if (code === 'log') {
            this.receiver.log();
        }
    }

}

class Invoker {
    private command: Command;
    constructor(command: Command) {
        this.command = command;
    }

    invoke(code: CommandCode) {
        this.command.execute(code);
    }
}

const receiver = new Receiver();
const command = new Command(receiver);
const invoker = new Invoker(command);


invoker.invoke("log");

命令模式还可以与组合模式配套使用

type CommandCode = 'log';


class Receiver {
    private name: string;
    constructor(name: string) {
        this.name = name;
    }
    log() {
        console.log(`${this.name}的Receiver 执行了`);
    }
}

class Command {
    private receiver: Receiver;

    constructor(receiver: Receiver) {
        this.receiver = receiver;
    }

    execute(code: CommandCode) {
        if (code === 'log') {
            this.receiver.log();
        }
    }
}

class CommandCompose {
    private commandList: Array<Command>;

    constructor() {
        this.commandList = [];
    }

    add(command: Command) {
        this.commandList.push(command);
    }

    execute(code: CommandCode) {
        this.commandList.forEach(command => {
            command.execute(code);
        })
    }
}

class Invoker {
    private commandCompose: CommandCompose;
    constructor(commandCompose: CommandCompose) {
        this.commandCompose = commandCompose;
    }

    invoke(code: CommandCode) {
        this.commandCompose.execute(code);
    }
}
// 命令1
const receiver1 = new Receiver("接受者1号");
const command1 = new Command(receiver1);

// 命令2
const receiver2 = new Receiver("接受者2号");
const command2 = new Command(receiver2);


const commandCompose = new CommandCompose();
commandCompose.add(command1)
commandCompose.add(command2)


const invoker = new Invoker(commandCompose);


invoker.invoke("log");

模板模式

子类实现抽象父类,并且完善抽象方法


abstract class Component {
    constructor() {
        this.init()
    }

    init() {
        this.render();
    }

    abstract render(): void;
}

class Input extends Component {
    render() {
        console.log("input");
    }
}

class Button extends Component {
    render() {
        console.log("button");
    }
}

迭代器模式

遍历出实体中的元素

class Iterator<T> {

    constructor(private data: Array<T>) {
        this.data = data;
    }

    next() {
        return {
            value: this.data.shift(),
            done: this.data.length === 0
        }
    }
}

let arrIterator = new Iterator([1, 2, 3, 4, 5]);

责任链模式

它允许一个请求通过一系列处理者(处理节点)进行传递,每个处理者都包含有对下一个处理者的引用,这样就会形成一个链表

abstract class Rule {


    protected next: Rule | null = null;

    abstract handle(value: string): boolean;

    public check(value: string,): Boolean {
        if (!this.handle(value)) {
            return false
        } else {
            return this.next?.check(value) ?? true;
        }
    }

    public setNext(rule: Rule) {
        this.next = rule;
        return rule
    }
}


class EmptyRule extends Rule {
    handle(value: string) {
        return value !== ''
    }
}


class MaxLengthRule extends Rule {
    handle(value: string) {
        return value.length < 10
    }
}

class MinLengthRule extends Rule {
    handle(value: string) {
        return value.length > 5
    }
}





let emptyRule = new EmptyRule();
let maxLengthRule = new MaxLengthRule();
let minLengthRule = new MinLengthRule();

// 

emptyRule.setNext(maxLengthRule.setNext(minLengthRule))
let flog = emptyRule.check("你asdas好")

console.log(flog);

创建型、结构型、行为型的区别

创建型结构型行为型
实体对实体单一实体一对一一对多
发挥的作用为创建一个实体做准备为已有实体修改属性不同实体之间如何交流