javascript设计模式浅析

132 阅读3分钟

一、设计模式的原则

1、开闭原则:开是拓展,闭是修改

2、单一职责原则:岗位职责单一,互不重叠

3、依赖倒置原则:上层不依赖下层实现

4、接口隔离原则

5、里氏替换原则:子类可以拓展,不能改变

二、创建型(创建元素)

1、工厂模式

隐藏创建过程,暴露共同接口

例子:游戏商店里下载初始化游戏,并且可以运行游戏

class Shop{
    create(name){
        return new Game(name)
    }
}

class Game{
    constructor(name){
        this.name = name
    }
    init(){
        console.log('init')
    }
    run(){
        console.log('run')
    }
}

const shop = new Shop()
const pubg = new Game('pubg')

pubg.init()
pubg.run()

2、建造者模式

独立生产商品,拆分简单模块,独立执行,注重过程和搭配

例子:优惠套餐单元,商品和皮肤进行打折售卖

class Product {
    constructor(name){
        this.name = name
    }
    init(){
        console.log('Product init')
    }
}

class Skin {
    constructor(name){
        this.name = name
    }
    init(){
        console.log('Skin init')
    }
}

class Shop {
    constructor(){
        this.package = '';
    }
    create(name){
       this.package = new PackageBuilder(name);
    }
    getGamePackage(){
        return this.package.getPackage();
    }
}

class PackageBuilder{
    constructor(name){
        this.game = new Product(name);
        this.skin = new Skin(name)
    }
    getPackage(){
        return this.game.init() + this.skin.init();
    }
}
// 每个模块独立解耦,而建造者负责创建串联正题系统

3、单例模式:

例:vue-router、vuex

// 全局只有一个实例,不能乱
class PlayStation {
    constructor(){
        this.state = 'off';
    }
    play(){
        if(this.state === 'on'){
            console.log('已经在happy了')
            return
        }
        this.state = 'on'
        console.log('开始happy了')
    }
    shutdown(){
        if(this.state === 'off'){
            console.log('已经关闭')
            return
        }
        this.state = 'off'
        console.log('已经关机,请放心')
    }
}

PlayStation.instance = undefined;
PlayStation.getInstance = (function(){
    return function(){
        if(!PlayStation.instance){
            PlayStation.instance = new PlayStation();
        }
        return PlayStation.instance;
    }()
})
cosnt ps1 = PlayStation.getInstance();
ps1.play();

cosnt ps1 = PlayStation.getInstance();
ps1.shutdown();

4、小结

模式场景:

  • 批量生产同类型应用来满足频繁使用同一种类型需求时—— 工厂模式

  • 当我们需要模块化拆分一个大的模块时,同时使模块间独立解耦分工—— 建造者模式

  • 全局只需要一个实例,注重统一一体化—— 单例模式 实际应用:

  • button producer:生产多个本质相同,利用传参区分不同属性的元素——>使用工厂

  • 全局应用 router store ->只需要一个实例->单例

  • 页头组件:Header:包含了title\button\breadcun->生产多重不同赖幸的元素-建造者模式

三、结构型(代码结构)

1、适配器模式

适配器:适配独立模块,保证模块间的独立解耦且链接兼容

例:需求:买了一个港行ps,插座是国标

class HKDevice{
    getPlug(){
        return '港行双圆柱插头'
    }
}

class Target{
    constructor(){
        this.plug = new HKDevice()
    }
    getPlug(){
        return this.plug.getPlug()+'转换器'
    }
}

const target = new Target();
target.getPlug();

2、装饰器模式:增强已有方案

动态将责任附加到对象上

例:设备升级

class Device{
    create(){
        console.log('PlayStation4')
    }
}

class Phone{
    create(){
        console.log('iPhone18')
    }
}

class Decorator{
    constructor(device){
        this.device = device;
    }
    create(){
        this.device.create();
        this.update(device);
    }
    update(device){
        console.log('device +'pro'')
    }
}

const device = new Device();
device.create()

const newDevice = new Decorator(device)
newDevice.create()

3、代理模式

使用代理人来替代原始对象 例:游戏防沉迷

class Game{
    play(){
        
    }
}

class Player{
    constructor(age){
        this.age = age
    }
}

class GameProxy{
    constructor(player){
        this.player = player
    }
    play(){
        return (this.player.age <16) ? "too young to play":new Game().play()
    }
}

const player = new Player(18);
const game = new GameProxy(player);

game.play();

4、小结

模式场景:

  • 中间转换参数,保持模块间独立的时候 ——适配器模式
  • 附着于多个组件上,批量动态赋予功能的时候——装饰器模式
  • 将代理对象与调用对象分离,不直接调用目标对象——代理模式

实际应用:

  • 两个模块:筛选器和表格,需要做一个联动。但筛选器的数据不能直接传入表格,需要做数据结构的转换;模块之间独立,需要做数据结构转换——适配器
  • 目前按钮、title、icon三个组件。希望开发一个模块,让三个组件同时具备相同的功能用一层装饰器,有统一的能力提升,且可以动态添加功能进行拓展——装饰器模式
  • ul中多个li,每个li上的点击事件,利用冒泡委托,事件绑定在ul上——代理模式

四、行为型(不同的对象之间划分责任和算法的抽象化)

1、命令模式

请求以命令的形式包裹在对象中,并传给调用对象

例:对于游戏角色的控制

//接受者
class Receiver {
    execute(){
        console.log('角色开始奔跑')
    }
}
// 触发者
class Operator {
    constructor(command){
        this.command = command;
    }
    run(){
        console.log('请给我爬')
        this.command.execute()
    }
}
//指令器
 class Command {
    constructor(receiver){
        this.receiver = receiver;
    }
    execute(){
        console.log('执行命令')
        this.receiver.executer()
    }
}

const soldier = new Receiver();
const order = new Command(soldier);
const player = new Operator(order)
player.run();

2、模板模式

基于编排,便于拓展,在模板中定义好方法的执行步骤,方法本身关注与自己的事情

例:成功吃个鸡,要分几步

class Device{
    constructor(){
        
    }
    powerOn(){
        console.log('打开电源')
    }
    login(){
         console.log('登录账号')
    }
    clickIcon(){
         console.log('点击开始游戏')
    }
    enterGame(){
         console.log('进入战场')
    }
    play(){
        this.powerOn();
        this.login();
        this.clickIcon();
        this.enterGame();
    }
}

3、观察者模式

当一个属性发生状态改变时,观察者会连续引发所有的相关状态改变

例:通过智能家居一键开始游戏

class MediaCenter{
    constructor(){
        this.state = '';
        this.observers = [];
    }
    attach(observer){
        this.observers.push(observer)
    }
    getState(){
        return this.state;
    }
    setState(state){
        this.state = state;
        this.notifyAllobservers();
    }
    notifyAllobservers(){
        this.observers.forEach(ob=>{
            ob.update();
        })
    }
}

class observer{
    constructor(name,center){
        this.name = name;
        this.center = center;
        this.center.attach(this);
    }
    update(){
        console.log(`${this.name} update, state:${this.center.getState}`)
    }
}

const center = new MediaCenter();
const ps = new Observer('ps',center)
const tv = new Observer('tv',center)

4、职责链

单向链执行,链式调用,职责独立 顺序执行

例:审批打游戏

class Action{
    constructor(name){
        this.name = name;
        this.nextAction = null;
    }
    setNextAction(action){
        this.nextAction = action;
    }
    handle(){
        console.log(`${this.name}请审批`)
        if(this.nextAction !== null){
            this.nextAction.handle();
        }
    }
}

const dad= new Action('ba')
const mam= new Action('mam')
const wife= new Action('wife')

dad.setNextAction(mom)
mom.setNextAction(wife)

dad.handle();

5、小结

模式场景:

  • 1、发出指令,中间层传递命令本身,命中包含执行对象——命令模式
  • 2、通过模板定义执行顺序,做独立操作——模板模式
  • 3、通过观察者,可以让被观察者值统一发生变化,触发相应依赖值的统一更新——观察者模式
  • 4、独立职责的单元通过链式执行,逐步操作流程——职责链

实际应用

  • 提交表单进行表单逐行校验,链式调用validate,依次执行——职责链
  • echart准备工作:canvas config init draw(),规划顺序执行——模板模式
  • 调度器在接受到一组新的数据时,解析数据,并且根据数据类型包裹在对象中传递到下级helper,再去执行响应操作——命令模式
  • 输入框输入的值去判断下拉框显示与否——观察者模式

五、设计模式经典面试题:

某停车场,分3层,每层100车位。每个车位都能监控到车辆的驶入和离开,

车辆进入前,显示每层的空余车位数量;

车辆进入时,摄像头可识别车牌号和时间;

车辆出来时,出口显示器显示车牌号和停车时长。

设计一套系统。

// 车辆
class car {
    constructor(num){
        this.num = num
    }
}
// 停车场,停车场有楼层、显示器、摄像机、拍下的车辆信息
class park {
    constructor(floors){
        this.floors = floors || []
        this.camera = new Camera()
        this.screen = new Screen()
        this.carList = {}
    }
    // 进
    in(car){
        //1、通过摄像头获取车辆信息
        const info = this.camera.shot(car)
        //2、将车停在停车场
        const  i = parseInt(Math.random()*100%100)
        const place = this.floors[0].places[i]
        place.in()
        info.place = place
        //3、将信息记录下来
        this.carList[car.num] = info
    }
    // 出
    out(car){
        // 获取信息
        const info = this.carList[car.num]
        // 清空车位
        const place = info.place
        place.out()
        // 显示时间
        this.screen.show(car, info.inTime)
        // 清空记录
        delete this.carList[car.num]
    }
    // 空余车位
    emptyNum(){
        return this.floors.map(floor=>{
            return `${floor.index}层还有${floor.emptyPlaceNum()}个空余车位`
        })
    }
}
// 每层
class Floor {
    constructor(index,places){
        this.index = index
        this.places = places || []
    }
    emptyPlaceNum(){
        let num = 0;
        this.places.forEach(p=>{
            if(p.empty){
                num = num +1
            }
        })
        return num
    }
}
// 停车位
class Place {
    constructor(empty){
        this.empty = true
    }
    in(){
        this.empty = false
    }
    out(){
        this.empty = true
    }
}
// 显示器
class Screen {
    show(car, inTime){
        console.log('车牌号',car.num)
        console.log('停车时间',Date.now() - inTime)
    }
}
// 摄像机,拍车牌号和时间
class Camera {
    shot(car){
        return {
            num: car.num,
            inTime:Date.now()
        }
    }
}

// 初始化停车场
const floors = []
for(let i=0;i<3;i++){
    const places =[]
    for(let j=0;j<100;j++){
        places[j] = new Place()
    }
    floors[i] = new Floor(i+1,places)
}

const park = new Park(floors)
// 初始化车辆
const car1 = new Car(100)
const car2 = new Car(200)
const car3 = new Car(300)