EP30-zake学设计模式

122 阅读4分钟

参考文档:

1,设计模式分类

1,创建型模式

提供了一种在创建对象的同时,隐藏创建逻辑的方式。而不是使用new运算符直接实例化对象。

  1. 工厂模式
  2. 抽象工厂模式
  3. 单例模式
  4. 建造者模式
  5. 原型模式
  6. 对象池模式
  7. 多例模式
  8. 静态工厂模式

2,结构型模式

关注类和对象的组合.

  1. 适配器模式
  2. 桥接模式
  3. 过滤器模式
  4. 组合模式
  5. 装饰器模式
  6. 外观模式
  7. 享元模式
  8. 代理模式
  9. 数据映射模式
  10. 依赖注入模式
  11. 门面模式
  12. 流接口模式
  13. 注册模式

3,行为型模式

关注对象间的通信

  1. 责任链模式
  2. 命令模式
  3. 解释器模式
  4. 迭代器模式
  5. 中介者模式
  6. 备忘录模式
  7. 观察者模式(面试常考)
  8. 发布-订阅模式(面试常考)
  9. 状态模式
  10. 空对象模式
  11. 策略模式
  12. 模板模式
  13. 访问者模式
  14. 规格模式
  15. 访问者模式

2,详解

参看文档:

  1. 前端渣渣唠嗑一下前端中的设计模式(真实场景例子)
  2. 【设计模式】这样学也太简单了吧!

1,创建型模式

封装对象的创建过程,将对象的创建和使用解耦。

1,单例模式

处理资源访问冲突,用来创建全局唯一对象。

//1,懒汉式:用到的时候才创建
//2,饿汉式:系统启动式创建。

2,工厂模式

用来创建继承同一父类,实现同一接口的子类对象,由给定的类型参数来创建具体的对象。

enum HelloType {
    A,
    B
}

interface Hello {
    sayHello()
}

class A implements Hello {
    sayHello() {
        console.log('A');
    }
}

class B implements Hello {
    sayHello() {
        console.log('B');
    }
}

class HelloFactory {
    static list = new Map<HelloType, Hello>([
        [HelloType.A, new A()],
        [HelloType.B, new B()]
    ])

    static getHello(type: HelloType) {
        return HelloFactory.list.get(type)
    }
}

// test
HelloFactory.getHello(HelloType.A).sayHello()
HelloFactory.getHello(HelloType.B).sayHello()

3,抽象工厂模式

继承同一父类,实现同一接口的子类对象,由给定的多个类型参数创建具体的对象。

enum Type {
    A,
    B
}

enum Occupation {
    TEACHER,
    STUDENT
}

interface Hello {
    sayHello()
}

interface Hello2{
    getHello(occupation: Occupation)
}

class TA implements Hello {
    sayHello() {
        console.log('Teacher A say hello')
    }
}

class TB implements Hello {
    sayHello() {
        console.log('Teacher B say hello')
    }
}

class SA implements Hello {
    sayHello() {
        console.log('Student A say hello')
    }
}

class SB implements Hello {
    sayHello() {
        console.log('Student B say hello')
    }
}

class AFactory implements Hello2 {
    static list = new Map<Occupation, Hello>([
        [Occupation.TEACHER, new TA()],
        [Occupation.STUDENT, new SA()]
    ])

    getHello(occupation: Occupation) {
        return AFactory.list.get(occupation)
    }
}

class BFactory {
    static list = new Map<Occupation, Hello>([
        [Occupation.TEACHER, new TB()],
        [Occupation.STUDENT, new SB()]
    ])

    static getHello(occupation: Occupation) {
        return BFactory.list.get(occupation)
    }
}

class HelloFactory {
    static list = new Map<Type, AFactory | BFactory>([
        [Type.A, AFactory],
        [Type.B, BFactory]
    ])

    static getType(type: Type) {
        return HelloFactory.list.get(type)
    }
}

// test
HelloFactory.getType(Type.A).getHello(Occupation.TEACHER).sayHello()
HelloFactory.getType(Type.A).getHello(Occupation.STUDENT).sayHello()
HelloFactory.getType(Type.B).getHello(Occupation.TEACHER).sayHello()
HelloFactory.getType(Type.B).getHello(Occupation.STUDENT).sayHello()

4,建造者模式

创建时有很多必填参数需要验证。
创建时参数求值有先后顺序,相互依赖。
创建有很多步骤,全部成功才能创建对象。

class Programmer {
  age: number
  username: string
  color: string
  area: string

  constructor(p) {
    this.age = p.age
    this.username = p.username
    this.color = p.color
    this.area = p.area
  }

  toString() {
    console.log(this)
  }
}

class Builder {
  age: number
  username: string
  color: string
  area: string

  build() {
    if (this.age && this.username && this.color && this.area) {
      return new Programmer(this)
    } else {
      throw new Error('缺少信息')
    }
  }

  setAge(age: number) {
    if (age > 18 && age < 36) {
      this.age = age
      return this
    } else {
      throw new Error('年龄不合适')
    }
  }

  setUsername(username: string) {
    if (username !== '小明') {
      this.username = username
      return this
    } else {
      throw new Error('小明不合适')
    }
  }

  setColor(color: string) {
    if (color !== 'yellow') {
      this.color = color
      return this
    } else {
      throw new Error('yellow不合适')
    }
  }

  setArea(area: string) {
    this.area = area
    return this
  }
}

// test
const p = new Builder()
  .setAge(20)
  .setUsername('小红')
  .setColor('red')
  .setArea('hz')
  .build()
  .toString()

5,原型模式

  • 1,原型模式是基于已有的对象克隆数据,而不是修改原型链。
  • 2,创建对象的代价太大,而同类的不同实例对象属性值基本一致,通过原型克隆的方式节约资源。
  • 3,不可变对象通过浅克隆实现。
  • 4,可变对象通过深克隆实现,深克隆占用资源多。
  • 5,

2,结构型设计模式

总结了一些类或者对象组合在一起的经典结构,这些经典结构可以解决特定应用场景的问题,将类或者对象的结构和使用解耦。

1,桥接模式

  • 1,将抽象和实现解耦,让他们可以独立变化。
  • 2,一个类存在多个独立变化的维度,通过组合的方式让多个维度可以独立的进行拓展。
  • 3,类似于组合优于继承原则。
enum MsgLevel {
    ERROR,
    WARN,
}

enum MsgType {
    EMAIL,
    PHONE
}

interface MsgContent {
    content()
}

class ErrorMsg implements MsgContent {
    content() {
        return 'ERROR'
    }
}

class WarnMsg implements MsgContent {
    content() {
        return 'WARN'
    }
}

interface MsgSender {
    send()
}

class PhoneSend implements MsgSender {
    msgContent: MsgContent

    constructor(msgContent: MsgContent) {
        this.msgContent = msgContent
    }

    send() {
        console.log(`phone send ${this.msgContent.content()}`)
    }
}

class EmailSend implements MsgSender {
    msgContent: MsgContent

    constructor(msgContent: MsgContent) {
        this.msgContent = msgContent
    }

    send() {
        console.log(`email send ${this.msgContent.content()}`)
    }
}

// test 此处还可以做成map结构继续优化(略)
new PhoneSend(new WarnMsg()).send()
new PhoneSend(new ErrorMsg()).send()
new EmailSend(new WarnMsg()).send()
new EmailSend(new ErrorMsg()).send()

2,代理模式

  • 1,给原类添加非功能性需求,为了将代码与原业务解耦。
  • 2,业务系统的非功能性需求:监控、统计、鉴权、限流、日志、缓存。
1,通过继承实现
class User {
    login() {
        console.log('user login...')
    }
}

class UserProxy extends User {
    login() {
        console.log('login before')
        super.login()
        console.log('login after')
    }
}
2,通过接口实现
interface Login {
    login()
}

class User implements Login {
    login() {
        console.log('user login...')
    }
}

class UserProxy implements Login {
    user = new User()

    login() {
        console.log('login before')
        this.user.login()
        console.log('login after')
    }
}

3,装饰器模式

  • 1,装饰器是对原始功能的增强。
  • 2,装饰器类和原始类继承同样的父类,可以对原始类嵌套多个装饰器类
  • 3,主要解决继承关系过于复杂的问题,通过组合来替代继承。
  • 4,
1,通过AOP实现
Function.prototype.before = function (beforeFn) {
    return (...arg) => {
        beforeFn(...arg);
        return this(...arg);
    }
};
Function.prototype.after = function (afterFn) {
    return (...arg) => {
        const result = this(...arg);
        afterFn(...arg);
        return result;
    }
};

function ImportEvent1() {
    console.log('重要的事情说三遍 1')
}

function ImportEvent2() {
    console.log('重要的事情说三遍 2')
}

function ImportEvent3() {
    console.log('重要的事情说三遍 3')
}

// test
let result = ImportEvent2.before(ImportEvent3).after(ImportEvent1)();
console.log(result);
2,

4,适配器模式

  • 1,适配器模式用于不就设计上的缺陷,将不兼容的接口变得兼容。
  • 2,适配不同格式的数据。
解决方案
  • 1,原接口方法不多,类适配器和对象适配器都可以。
  • 2,原接口方法很多,并且和目标接口差异小,使用类适配器减少代码量。
  • 3,原接口方法很多,并且和目标接口差异大,用对象适配器,组合由于继承。
// 目标接口格式
interface ITarget {
    f1()

    f2()

    f3()
}

// 原有类与目标接口不兼容
class Origin {
    fa() {
    }

    fb() {
    }

    f3() {
    }
}

// 使用适配器来兼容
class Adaptor implements ITarget {
    origin = new Origin()

    f1() {
        this.origin.fa()
    }

    f2() {
        this.origin.fb()
    }

    f3() {
        this.origin.f3()
    }
}

5,享元模式

1,应用场景

共享的单元。复用对象,节省内存,前提是享元对象是不可变对象(初始化之后不可改变)。

6,组合模式

1,应用场景

将一组对象组织成树形结构,以表示一种部分整体的层次结构。组合模式可以让客户端统一单个对象和组合对象的处理逻辑。

3,行为型设计模式

1,观察者模式

1,应用场景
  • 1,将观察者和被观察者解耦
  • 2,发布订阅模式有发布订阅中心(中间商),观察者模式没有。
2,实现
// 目标对象
class Subject {
    observerList: Observer[]

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

    addObserver(observer) {
        this.observerList.push(observer);
    }

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

// 观察者
class Observer {
    cb: Function

    constructor(cb: Function) {
        if (typeof cb === "function") {
            this.cb = cb;
        } else {
            throw new Error("Observer构造器必须传入函数类型!");
        }
    }

    update() {
        this.cb();
    }
}


// test
const observerCallback = function () {
    console.log("我被通知了");
};
const observer = new Observer(observerCallback);
const subject = new Subject();
subject.addObserver(observer);
subject.notify();