zoukankan      html  css  js  c++  java
  • JS 设计模式 (待整理)

    面试敲门砖、进阶垫脚石、设计有模式、代码更合理

    Javascript 设计模式系统讲解与应用 (练习代码)

    第2章 面向对象

    2-1 搭建开发环境

    npm init

    npm i webpack webpack-cli --save-dev

    //add webapck.dev.config.js file
    module.exports = {
      entry: './src/index.js',
      output: {
        path: __dirname,
        filename: './release/bundle.js'
      }
    }
    
    # package.json
    "dev": "webpack --config ./webpack.dev.config.js --mode development"
    

    npm i webpack-dev-server html-webpack-plugin -D

    // webapck.dev.config.js
    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: __dirname,
        filename: './release/bundle.js'
      },
    
      plugins: [
        new HtmlWebpackPlugin({
          template: './index.html'
        })
      ],
    
      devServer: {
        contentBase: path.join(__dirname, './release'), // 设置根目录
        open: true,  // 自动打开浏览器
        port: 9000
      }
    }
    

    "dev": "webpack-dev-server --config ./webpack.dev.config.js --mode development"

    支持babel npm i babel-core babel-loader babel-polyfill babel-preset-es2015 babel-preset-latest -D

    新增babel配置文件 .babelrc

    {
        "presets": ["es2015", "latest"],
        "plugins": []
    }
    

    再次修改webpack.config.js

    // 新增module
    module: {
      rules: [{
        test: /.js?$/,  // 我们要去检验哪些文件
        exclude: /(node_modules)/,  // 跳过哪些文件
        loader: 'babel-loader'  // 使用的loader
      }]
    },
    

    2-2 什么是面向对象

    2-3 面向对象-继承

    2-4 面向对象-封装

    2-5 面向对象-多态

    2-6 面向对象-应用举例

    2-7 面向对象-总结

    2-8 UML类图1-介绍

    2-9 UML类图2-关系

    2-10 总结

    第3章 设计原则

    3-1 设计原则-介绍

    • 即按照哪一种思路或者标准来实现功能
    • 功能相同,可以有不同的设计方案来实现
    • 伴随着需求的增加,设计的作用才能体现出来

    3-2 设计原则-何为设计

    • 设计准则:

      • 1 小既是美
      • 2 让每个程序只做好一件事
      • 3 快速建立原型
      • 4 舍弃高效率而取可移植性
      • 5采用纯文本来存储数据
      • 6 充分利用软件的杠杆效应(软件复用)
      • 7 使用shell脚本来提高杠杆效应和可移植性
      • 8 避免强制性的用户界面
      • 9 让每一个程序都称为过滤器
    • 小准则:

      • 允许用户定制环境
      • 尽量使操作系统内核小而轻量化
      • 使用小写字母并尽量简短
      • 沉默是金
      • 各部分之和大于整体
      • 寻求 90% 的解决方案

    源于:《UNIX/LINUX 设计思想》

    3-3 设计原则-5大原则

    S O L I D 五大设计原则
    
    • S - 单一责任原则
    • O - 开放封闭原则
    • L - 里氏替换原则
    • I - 接口独立原则
    • D - 依赖倒置原则

    单一责任原则

    • 一个程序只做好一件事
    • 如果功能过于复杂就拆分,每个部分保持独立

    开放封闭原则

    • 对扩展开发,对修改封闭
    • 增加需求时,扩展新代码,而非修改已有代码
    • 这个是软件设计的终极目标

    里氏替换原则

    • 子类能覆盖父类
    • 父类能出现的地方子类就能出现
    • JS中使用较少(弱类型&继承使用较少)

    接口独立原则

    • 保持接口的单一独立,避免出现 “胖接口”
    • JS中没有接口(typescript例外),使用较少
    • 类似于单一职责原则,这里更关注接口

    依赖倒置原则

    • 面向接口编程,依赖于抽象而不依赖于具体
    • 使用方只关注接口而不关注具体类的实现
    • JS中使用较少

    设计原则总结

    • S O 体现较多,详细介绍
    • LID 体现较少,但是要了解其用意

    用Promise来说明 S-O

    function loadImg(src) {
      var promise = new Promise(function(resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
          resolve(img)
        }
        img.onerror = function () {
          reject('图片加载失败')
        }
        img.src = src
      })
      return promise
    }
    
    var src = 'https://www.imooc.com/static/img/index/logo.png'
    var result = loadImg(src)
    
    result.then(function (img) {
      console.log('img.width', img.width)
      return img
    }).then(function (img) {
      console.log('img.height', img.height)
    }).catch(function (err) {
      console.error(err)
    })
    
    • 单一职责原则:每个 then 中的逻辑只做好一件事
    • 开放封闭原则:如果新增需求,扩展then
    • 对扩展开发,对修改封闭

    3-4 用promise演示

    就是3-3的代码

    3-5 设计模式简介

    从设计到模式

    体会什么是设计?设计是设计,模式是模式,两者是分离的。

    该如何学习设计模式?

    • 明白每个设计的道理和用意
    • 通过经典应用体会它的真正使用场景
    • 自己编码时多思考,尽量模仿

    3-6 23种设计模式介绍

    其实设计模式大致分为三种类型:

    • 创建型
    • 组合型
    • 行为型

    这23种设计模式分别分散在这三种类型中。

    创建型

    • 工厂模式(工厂方法模式、抽象工厂模式、建造者模式)
      • 工厂模式是讲怎么面向对象、怎么创建对象、怎么生成
    • 单例模式
      • 单例模式是讲如果这个系统中只有一个指定对象,那么出现第二个时,该怎么办
    • 原型模式
      • 原型模式是讲如何通过一个现有的对象,用拷贝的方式来生成另一个新的对象

    结构型

    • 适配器模式
    • 装饰器模式
    • 代理模式
    • 外观模式
    • 桥接模式
    • 组合模式
    • 享元模式

    行为型

    • 策略模式
    • 模板方法模式
    • ★观察者模式★
    • 迭代器模式
    • 职责链模式
    • 命令模式
    • 备忘录模式
    • ★状态模式★
    • 访问者模式
    • 中介模式
    • 解释器模式

    如何讲解设计模式

    • 介绍和举例(生活中易理解的示例)
    • 画UML类图写demo代码
    • 结合经典应用场景,讲解该设计模式如何被使用

    3-7 面试真题1

    • 打车时,可以打专车或者快车。任何车都有车牌号和名称
      • 解析:需设计公共父类(车牌号和名称),父类下又有两子类(专车和快车))
    • 不同车价格不同,快车每公里1元,专车每公里2元
      • 解析:子类里有不同的价格
    • 行程开始时,显示车辆信息
      • 行车和车有关系,但和专车还是快车没关系。所以我们需要依赖抽象编程,所以行程只和车有关系,不和具体哪种车有关,也就是说无论什么车都有行车信息
      • 所以我们需要再建一个"行程"的类,这个类引用车的某个属性,我们可以通过这个属性得到车的信息(车牌号、名称、单价)
    • 行程结束时,显示打车金额(假定行程就5公里)
      • “金额”属于行程。买了一万辆车丢着是没有行程金额的

    UML类图

    uml01

    class Car {
      constructor(number, name) {
        this.number = number
        this.name = name
      }
    }
    
    class Kuaiche extends Car {
      constructor(number, name) {
        super(number, name)
        this.Price = 1
      }
    }
    
    class Zhuanche extends Car {
      constructor(number, name) {
        super(number, name)
        this.Price = 2
      }
    }
    
    class Trip {
      constructor(car) {
        this.car = car
      }
    
      start() {
        console.log(`行程开始,名称:${this.car.name},车牌号:${this.car.Price}`)
      }
    
      end() {
        console.log(`行程结束,价格:${this.car.Price * 5}`)
      }
    }
    
    let car = new Kuaiche('101', '捷达')
    let trip = new Trip(car)
    trip.start()
    trip.end()
    

    3-8 面试真题2

    • 某停车场,分3层,每层100车位
      • 解析:三个类,分别是停车场、每层、车位,三个class
    • 每个车位都能监控到车辆的驶入和离开
      • 解析:我们要给车位这个类定义一个方法或者属性来监控车辆驶入和离开,这个监控的方法要改变车位这个类的一个状态,车位空不空
    • 车辆进入前,显示每层的空余车位数量
      • 解析:车辆进入前肯定面对的是停车场这个类,所以这个信息要在停车场这个类中释放出来,所以我们加一个方法,动态计算显示每一层(每一层都是一个类的实例)空车位,所以层这个类里还得加显示空车位的方法,最终由停车场这个类累加后显示
    • 车辆进入时,摄像头可以识别车牌号和时间
      • 解析:还得加摄像头的class,这个class有方法能识别出车牌号和记录驶入时间,也就是说摄像头这个类,输入的是车的实例,输出车牌号和时间,这个车牌号和时间要让停车场那个类里去存,所以停车场这个类还得加车辆列表的属性
    • 车辆出来时,出口显示器显示车牌号和停车时长
      • 解析:还得加显示器的类,通过显示器拿到车牌号和记录的驶入时间,然后用当前时间减去这个事件就拿到了停车时长

    uml02

    // 车
    class Car {
        constructor(num) {
            this.num = num
        }
    }
    
    // 入口摄像头
    class Camera {
        shot(car) {
            return {
                num: car.num,
                inTime: Date.now()
            }
        }
    }
    
    // 出口显示器
    class Screen {
        show(car, inTime) {
            console.log('车牌号', car.num)
            console.log('停车时间', Date.now() - inTime)
        }
    }
    
    // 停车场
    class Park {
        constructor(floors) {
            this.floors = floors || []
            this.camera = new Camera()
            this.screen = new Screen()
            this.carList = {}
        }
        in(car) {
            // 获取摄像头的信息:号码 时间
            const info = this.camera.shot(car)
            // 停到某个车位
            const i = parseInt(Math.random() * 100 % 100)
            const place = this.floors[0].places[i]
            place.in()
            info.place = place
            // 记录信息
            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()} 个车位`
            }).join('
    ')
        }
    }
    
    // 层
    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() {
            this.empty = true
        }
        in() {
            this.empty = false
        }
        out() {
            this.empty = true
        }
    }
    
    // 测试代码------------------------------
    // 初始化停车场
    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('A1')
    const car2 = new Car('A2')
    const car3 = new Car('A3')
    
    console.log('第一辆车进入')
    console.log(park.emptyNum())
    park.in(car1)
    console.log('第二辆车进入')
    console.log(park.emptyNum())
    park.in(car2)
    console.log('第一辆车离开')
    park.out(car1)
    console.log('第二辆车离开')
    park.out(car2)
    
    console.log('第三辆车进入')
    console.log(park.emptyNum())
    park.in(car3)
    console.log('第三辆车离开')
    park.out(car3)
    

    3-9 总结

    第4章 工厂模式

    原理

    • new 操作单独封装
    • 遇到 new 时,就要考虑是否该使用工厂模式

    示例

    • 你去购买汉堡,直接点餐、取餐,不会自己亲手做
    • 商店要 “封装” 做汉堡的工作,做好直接给顾客
    /**
     * 工厂模式示例,逻辑如图:
     *
     * --------------------------    ----------------|
     * |       Creator          |    |  Product      |
     * |------------------------|    |---------------|
     * |                        |    | + name:String |
     * |------------------------| -> |---------------|
     * | + create(name):Product |    | + init()      |
     * --------------------------    | + fn1()       |
     *                               | + fn2()       |
     *                               ----------------|
     */
    
    class Product {
        constructor(name) {
            this.name = name;
        }
    
        init() {
            console.log("init", this.name);
        }
    
        fn1() {
            console.log("fn1", this.name);
        }
    
        fn2() {
            console.log("fn2", this.name);
        }
    }
    
    class Creator {
        create(name) {
            return new Product(name);
        }
    }
    
    // 测试
    const creator = new Creator();
    const p1 = creator.create("test1");
    const p2 = creator.create("test2");
    
    p1.init();
    p2.init();
    p1.fn1();
    p2.fn1();
    p1.fn2();
    p2.fn2();
    

    场景

    • jQuery - $('div')
    • React.createElement
    • vue异步组件

    React.createElement

    react1

    React.createElement使用工厂模式的好处:如果我们不用 createElement 封装 new VNode(tag,attrs, children),在对生成VNode示例时我们还是让用户去验证各个属性参数,显示不合理,而且用了工厂模式后用户根本不关系内部构造函数怎么变化。

    vue异步组件

    vue

    Vue异步加载组件完成后创建组件的模式

    使用工厂模式把工厂内部构造函数与用户隔离

    设计原则验证

    • 构造函数和创建者分离
    • 符合开放封闭原则

    第5章 单例模式

    • 系统中仅被唯一使用的
    • 一个类只有一个实例
    /**
     * 单例模式
     */
    
    class SingleObject {
        login() {
            console.log("login...");
        }
    }
    
    // 创建一个静态自执行的方法
    SingleObject.getInstance = (function() {
        let instance;
        return function() {
            if (!instance) {
                instance = new SingleObject();
            }
    
            return instance;
        }
    })()
    
    
    // 测试
    let obj1 = SingleObject.getInstance();
    obj1.login();
    let obj2 = SingleObject.getInstance();
    obj2.login();
    
    console.log(obj1 === obj2);
    

    示例

    • 登录框
    class  LoginForm() {
    
        constructor() {
        
            this.state  =  'hide'
        
        }
        
        hide() {
        
            if(this.state  ===  'hide'){
            
                console.log('已经隐藏')
            
                return
            
            }
            
            this.state  ==  'hide'
            
            consoel.log('隐藏成功')
        
        }
        
        show() {
        
            if(this.state  ===  'show'){
            
                console.log('已經顯示')
                
                return
            
            }
            
            this.state  ===  'show'
            
            console.log('顯示成功')
        
        }
    
    }
    
    LoginForm.instance  = (function(){
    
        let  instance
        
        return  function(){
        
        if(!instance){
            instance  =  new  LoginForm()
        }
        
        return  instance
    
    }
    
    })()
    
    let  login1  =  LoginForm.instance()
    
    login1.hide()
    
    let  login2  =  LoginForm.instance()
    
    login2.hide()
    
    • 购物车
    • vuex和redux中的store

    single

    js-single

    jQuery永远只有一个

    设计 原则 验证

    • 符合单一职责原则,只实例化唯一的对象
    • 没法具体开放封闭原则,但是绝对不违反开放封闭原则

    第6章 适配器模式

    • 就接口格式和使用者不兼容
    • 中间加一个适配器接口

    dd

    d1

    /**
     * 适配器模式
     */
    
    class Adapter {
        specificRequest() {
            return "旧的接口内容"
        }
    }
    
    class Target {
        constructor() {
            this.adapter = new Adapter();
        }
    
        request() {
            let info = this.adapter.specificRequest();
            return `${info} - 处理... - 新的接口内容`;
        }
    }
    
    // 测试
    let target = new Target();
    const r = target.request();
    console.log(r);
    

    场景

    • 封装旧接口
    • Vue的computed

    设计原则验证

    • 将就借口和使用者进行分离
    • 符合开放封闭原则

    第7章 装饰器模式

    • 为对象添加新功能
    • 不改变其原有的结构和功能
    • 将现有对象和装饰器进行分离,两者独立存在
    /**
     * 装饰器模式
     */
    
    class Circle {
        draw() {
            console.log("画一个圆");
        }
    }
    
    class Decorator {
        constructor(circle) {
            this.circle = circle;
        }
    
        draw() {
            this.circle.draw();
            this.setRedBorder(this.circle);
        }
    
        setRedBorder(circle) {
            console.log("设置红色边框");
        }
    }
    
    // 测试
    let c = new Circle();
    c.draw();
    
    let d = new Decorator(c);
    d.draw();
    

    第8章 代理模式

    8-1 代理模式-介绍和演示

    • 使用者无权访问目标对象
    • 中间加代理,通过代理做授权和控制
    • 代理类与目标类分离,隔离开目标类和使用者

    8-2 代理模式-场景1(事件代理和jq的proxy)

    8-3 代理模式-场景2(明星经纪人)

    /**
     * 代理模式
     */
    
    class ReadImg {
        constructor(filename) {
            this.filename = filename;
            this.loadFromDisk();
        }
    
        loadFromDisk() {
            console.log("从硬盘加载数据" + this.filename);
        }
    
        display() {
            console.log("显示数据" + this.filename);
        }
    }
    
    class ProxyImg {
        constructor(filename) {
            this.realImg = new ReadImg(filename);
        }
    
        display() {
            this.realImg.display();
        }
    }
    
    
    // test
    let proxyImg = new ProxyImg("1.png");
    proxyImg.display();
    
    
    // =================================
    
    /**
     * 使用ES6语法的Proxy类演示 代理模式的示例,明星 - 经纪人
     */
    
    let star = {
        name: "张xx",
        age : 25,
        phone: "138123456789"
    }
    
    let agent = new Proxy(star, {
        get: function(target, key) {
            if (key === "phone") {
                return "agent phone: 13555555555";
            }
            else if (key === "price") {
                return 150000;
            }
            return target[key];
        },
        set: function(target, key, val) {
            if (key === "customPrice") {
                if (val < 10000) {
                    throw new Error("价格太低");
                } else {
                    target[key] = val;
                    return true;
                }
            }
        }
    })
    
    // test
    console.log(agent.name);
    console.log(agent.phone);
    console.log(agent.age);
    console.log(agent.price);
    
    agent.customPrice = 120000; // OK
    console.log(agent.customPrice);
    
    agent.customPrice = 1000; // Error
    console.log(agent.customPrice);
    

    8-4 代理&适配器&装饰模式对比

    • 代理模式VS适配器模式
      • 适配器模式:提供一个不同的接口(如不同版本的插头)
      • 代理模式:提供一模一样的接口
    • 代理模式VS装饰器模式
      • 装饰器模式:扩展功能,原有功能不变且可直接使用
      • 代理模式:显示原有的功能,但是经过限制或者阉割之后的

    第9章 外观模式

    9-1 外观模式

    • 为子系统中的一组接口提供了一个高层接口
    • 使用者使用高层接口
    • 不符合单一职责原则和开放封闭原则,因此谨慎使用,不可滥用

    第10章 观察者模式

    10-1 观察者模式-介绍和演示

    • 发布 订阅
    • 一对多
    • 主题和观察者分离,不是主动触发而是被动监听,两者解耦

    10-2 观察者模式-场景1jquery

    10-3 观察者模式-场景2NodeJs自定义事件

    /**
     * 观察者模式,使用nodejs的events模块的示例
     */
    
    const EventEmitter = require("events").EventEmitter;
    
    // =========EventEmitter的基础用法=============
    const emitter1 = new EventEmitter();
    
    // 监听some事件
    emitter1.on("some", info => {
        console.log("fn1", info);
    })
    
    // 监听some事件
    emitter1.on("some", info => {
        console.log("fn2", info);
    })
    
    // 触发some事件
    emitter1.emit("some", "xxxx");
    
    // =============================================
    
    // 下面使用继承来实现EventEmitter
    class Dog extends EventEmitter {
        constructor(name) {
            super();
            this.name = name;
        }
    }
    
    let dog = new Dog("dog");
    dog.on("bark", function() {
        console.log(this.name, " barked-1");
    })
    
    dog.on("bark", function() {
        console.log(this.name, " barked-2");
    })
    
    setInterval(() => {
        dog.emit("bark")
    }, 1000);
    
    

    10-4 观察者模式-其它场景

    /**
     * 观察者模式
     */
    
    // 主题,保存状态,状态变化之后触发所有观察者对象
    class Subject {
        constructor() {
            this.state = 0;
            this.observers = [];
        }
    
        getState() {
            return this.state;
        }
    
        setState(state) {
            this.state = state;
            this.notifyAllObservers();
        }
    
        notifyAllObservers() {
            this.observers.forEach(observer => {
                observer.update();
            })
        }
    
        attach(observer) {
            this.observers.push(observer);
        }
    }
    
    // 观察者
    class Observer {
        constructor(name, subject) {
            this.name = name;
            this.subject = subject;
            this.subject.attach(this);
        }
    
        update() {
            console.log(`${this.name} update! state is: ${this.subject.state}`);
        }
    }
    
    
    // 测试
    let s = new Subject();
    let o1 = new Observer("o1", s);
    let o2 = new Observer("o2", s);
    let o3 = new Observer("o3", s);
    let o4 = new Observer("o4", s);
    
    s.setState(1);
    s.setState(2);
    

    第11章 迭代器模式

    11-1 迭代器模式-介绍

    • 顺序访问一个集合
    • 使用者无需知道集合的内部结构(封装)
    • 迭代器对象与目标对象分离
    • 迭代器将使用者与目标对象隔离开

    11-2 迭代器模式-演示

    11-3 迭代器模式-场景1(ES6 Iterator)

    11-4 迭代器模式-场景2

    11-5 迭代器模式-代码演示和总结

    /**
     * 迭代器模式
     */
    
    class Iterator {
        constructor(container) {
            this.list = container.list;
            this.index = 0;
        }
    
        next() {
            if (this.hasNext()) {
                return this.list[this.index++];
            }
            return null;
        }
    
        hasNext() {
            if (this.index >= this.list.length) {
                return false;
            }
            return true;
        }
    }
    
    class Container {
        constructor(list) {
            this.list = list;
        }
    
        // 生成遍历器
        getIterator() {
            return new Iterator(this);
        }
    }
    
    
    // 测试
    const arr = [1, 2, 3, 4, 5];
    let container = new Container(arr);
    let it = container.getIterator();
    while(it.hasNext()) {
        console.log(it.next());
    }
    
    // ============= 使用ES6的迭代器生成 =============
    function each(data) {
        // 生成遍历器
        let it = data[Symbol.iterator]();
    
        let item;
        do {
            // 遍历器生成可迭代内容,包含value和done属性,
            // 其中done属性代替自定义的hasNext()方法,
            // false表示还有数据,true则表示已经迭代完成
            item = it.next();
            if (!item.done) {
                console.log(item.value);
            }
        } while (!item.done);
    }
    
    // ES6的Iterator已经封装在了语法 for...of 中,直接使用即可
    function each2(data) {
        for (const item of data) {
            console.log(item);
        }
    }
    
    
    // 测试
    const arr2 = [10, 20, 30, 40, 50, 60];
    let m = new Map();
    m.set("a", 100);
    m.set("b", 200);
    m.set("c", 300);
    
    each(arr2);
    each(m);
    
    each2(arr2);
    each2(m);
    
    

    第12章 状态模式

    12-1 状态模式-介绍和演示

    • 允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类
    /**
     * 状态模式
     */
    
    // 模拟红绿灯状态
    class State {
        constructor(color) {
            this.color = color;
        }
    
        handle(context) {
            console.log(`切换到 ${this.color} `);
            context.setState(this);
        }
    }
    
    // 主体
    class Context {
        constructor() {
            this.state = null;
        }
    
        getState() {
            return this.state;
        }
    
        setState(state) {
            this.state = state;
        }
    }
    
    
    // 测试
    let context = new Context();
    let green = new State("绿灯");
    let yellow = new State("黄灯");
    let red = new State("红灯");
    
    // 绿灯亮
    green.handle(context);
    console.log(context.getState());
    // 黄灯亮
    yellow.handle(context);
    console.log(context.getState());
    // 红灯亮
    red.handle(context);
    console.log(context.getState());
    
    

    12-2 状态模式-场景1(有限状态机)

    12-3 状态模式-场景2(写一个promise)

    import * as fs from "fs";
    import * as StateMachine  from 'javascript-state-machine';
    import * as request from 'request';
    
    
    // promise state: resolve(pending => fullfilled), reject(pending => rejected)
    const fsm = new StateMachine({
      init: 'pending',
      transitions: [
        {
          name: 'resolve',
          form: 'pending',
          to: 'fullfilled'
        }, {
          name: 'reject',
          from: 'pending',
          to: 'rejected'
        }
      ],
      methods: {
        onResolve: function(state, data, data1) {
          // sate 当前状态机实例;data fsm.resolve(xxx) 传递的参数
          // console.log(state, data)
          data.succFnList.forEach(fn => fn(data1));
        },
        onReject: function(state, data) {
          data.failFnList.forEach(fn => fn());
        }
      },
    
    });
    
    
    class MyPromise {
    
      succFnList: any[];
      failFnList: any[];
    
      constructor(fn) {
        this.succFnList = [];
        this.failFnList = [];
    
        fn((data) => {
          // resolve 函数
          fsm.resolve(this, data);
        },
        () => {
          // reject 函数
          fsm.reject(this);
        });
      }
    
      then(succFn, failFn) {
        this.succFnList.push(succFn);
        this.failFnList.push(failFn);
      }
    
    }
    
    
    function downloadImg(src) {
      const promise = new MyPromise(function(resolve, reject) {
        request(src, function(error, response, body) {
          if (error) {
            reject();
          }
          resolve(body);
        })
      });
    
      return promise;
    }
    
    const imgSrc = 'https://www.npmjs.com/package/javascript-state-machine';
    
    const imgPromise = downloadImg(imgSrc);
    imgPromise.then(function(data) {
      console.log(fsm.state)
      fs.writeFileSync('./test.html', data)
    }, function(error) {
      console.log(error);
    });
    

    第13章 其他设计模式

    13-1 其他设计模式概述

    13-2 原型模式

    • 原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。
    /**
     * 原型模式
     * prototype可以理解为ES6中class的一种底层原理,但是class是实现面向对象的基础,并不是服务于某个模式
     */
    
    // 创建一个原型
    let prototype = {
        getName: function() {
            return this.first + " " + this.last;
        },
    
        say: function() {
            console.log("Hello!");
        }
    }
    
    // 基于原型创建x
    let x = Object.create(prototype);
    x.first = "A";
    x.last = "B";
    console.log(x.getName());
    x.say();
    
    // 基于原型创建y
    let y = Object.create(prototype);
    y.first = "C";
    y.last = "D";
    console.log(y.getName());
    y.say();
    

    13-3 桥接模式

    • 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
    /**
     * 桥接模式
     */
    
    class Color {
        constructor(name) {
            this.name = name;
        }
    }
    
    class Shape {
        constructor(name, color) {
            this.name = name;
            this.color = color;
        }
    
        draw() {
            console.log(`使用${this.color.name}颜色画了一个${this.name}`);
        }
    }
    
    
    // 测试
    let red = new Color("red");
    let yellow = new Color("yellow");
    let circle = new Shape("circle", red);
    circle.draw();
    
    let triangle = new Shape("triangle", yellow);
    triangle.draw();
    

    13-4 组合模式

    • 将对象组合成树形结构,以表示“整体-部分”的层次结构。
    • 通过对象的多态表现,使得用户对单个对象和组合对象的使用具有一致性。
    class TrainOrder {
        create () {
            console.log('创建火车票订单')
        }
    }
    class HotelOrder {
        create () {
            console.log('创建酒店订单')
        }
    }
    
    class TotalOrder {
        constructor () {
            this.orderList = []
        }
        addOrder (order) {
            this.orderList.push(order)
            return this
        }
        create () {
            this.orderList.forEach(item => {
                item.create()
            })
            return this
        }
    }
    // 可以在购票网站买车票同时也订房间
    let train = new TrainOrder()
    let hotel = new HotelOrder()
    let total = new TotalOrder()
    total.addOrder(train).addOrder(hotel).create()
    
    

    13-5 享元模式

    • 运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式
    let examCarNum = 0         // 驾考车总数
    /* 驾考车对象 */
    class ExamCar {
        constructor(carType) {
            examCarNum++
            this.carId = examCarNum
            this.carType = carType ? '手动档' : '自动档'
            this.usingState = false    // 是否正在使用
        }
    
        /* 在本车上考试 */
        examine(candidateId) {
            return new Promise((resolve => {
                this.usingState = true
                console.log(`考生- ${ candidateId } 开始在${ this.carType }驾考车- ${ this.carId } 上考试`)
                setTimeout(() => {
                    this.usingState = false
                    console.log(`%c考生- ${ candidateId } 在${ this.carType }驾考车- ${ this.carId } 上考试完毕`, 'color:#f40')
                    resolve()                       // 0~2秒后考试完毕
                }, Math.random() * 2000)
            }))
        }
    }
    
    /* 手动档汽车对象池 */
    ManualExamCarPool = {
        _pool: [],                  // 驾考车对象池
        _candidateQueue: [],        // 考生队列
    
        /* 注册考生 ID 列表 */
        registCandidates(candidateList) {
            candidateList.forEach(candidateId => this.registCandidate(candidateId))
        },
    
        /* 注册手动档考生 */
        registCandidate(candidateId) {
            const examCar = this.getManualExamCar()    // 找一个未被占用的手动档驾考车
            if (examCar) {
                examCar.examine(candidateId)           // 开始考试,考完了让队列中的下一个考生开始考试
                  .then(() => {
                      const nextCandidateId = this._candidateQueue.length && this._candidateQueue.shift()
                      nextCandidateId && this.registCandidate(nextCandidateId)
                  })
            } else this._candidateQueue.push(candidateId)
        },
    
        /* 注册手动档车 */
        initManualExamCar(manualExamCarNum) {
            for (let i = 1; i <= manualExamCarNum; i++) {
                this._pool.push(new ExamCar(true))
            }
        },
    
        /* 获取状态为未被占用的手动档车 */
        getManualExamCar() {
            return this._pool.find(car => !car.usingState)
        }
    }
    
    ManualExamCarPool.initManualExamCar(3)          // 一共有3个驾考车
    ManualExamCarPool.registCandidates([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])  // 10个考生来考试
    
    

    13-6 策略模式

    • 定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换
    /**
     * 策略模式
     */
    
    // 普通情况下,没有使用策略模式
    class User {
        constructor(type) {
            this.type = type;
        }
    
        buy() {
            if (this.type === "ordinary") {
                console.log("普通用户购买");
            } else if (this.type === "member") {
                console.log("会员用户购买");
            } else if (this.type === "vip") {
                console.log("高级会员购买");
            }
        }
    }
    
    // 使用
    let u1 = new User("ordinary");
    u1.buy();
    let u2 = new User("member");
    u2.buy();
    let u3 = new User("vip");
    u3.buy();
    
    
    // ================ 使用策略模式进行调整 ===================
    class OrdinaryUser {
        buy() {
            console.log("普通用户购买");
        }
    }
    
    class MemberUser {
        buy() {
            console.log("会员用户购买");
        }
    }
    
    class VipUser {
        buy() {
            console.log("高级会员用户购买");
        }
    }
    
    
    // 测试
    let ou = new OrdinaryUser();
    ou.buy();
    let mu = new MemberUser();
    mu.buy();
    let vu = new VipUser();
    vu.buy();
    

    13-7 模板方法模式和职责连模式

    /**
     * 职责链模式
     */
    
    class Action {
        constructor(name) {
            this.name = name;
            this.nextAction = null;
        }
    
        setNextAction(action) {
            this.nextAction = action;
        }
    
        handle() {
            console.log(`${this.name} 执行了操作`);
            if (this.nextAction) {
                this.nextAction.handle();
            }
        }
    }
    
    
    // 测试
    let a1 = new Action("组长");
    let a2 = new Action("经理");
    let a3 = new Action("总监");
    a1.setNextAction(a2);
    a2.setNextAction(a3);
    a1.handle();
    

    13-8 命令模式

    • 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
    
    /**
     * 命令模式
     */
    
    class Receiver {
        exec() {
            console.log("执行");
        }
    }
    
    class Command {
        constructor(receiver) {
            this.receiver = receiver;
        }
    
        cmd() {
            console.log("触发命令");
            this.receiver.exec();
        }
    }
    
    class Invoker {
        constructor(command) {
            this.command = command;
        }
    
        invoke() {
            console.log("开始");
            this.command.cmd();
        }
    }
    
    
    // 测试
    let soldier = new Receiver();
    let trumpeter = new Command(soldier);
    let general = new Invoker(trumpeter);
    general.invoke();
    

    13-9 备忘录模式

    • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
    /**
     * 备忘录模式
     */
    
    // 备忘类
    class Memento {
        constructor(content) {
            this.content = content;
        }
    
        getContent() {
            return this.content;
        }
    }
    
    // 备忘列表
    class CareTaker {
        constructor() {
            this.list = [];
        }
    
        add(memento) {
            this.list.push(memento);
        }
    
        get(index) {
            return this.list[index];
        }
    }
    
    // 编辑器
    class Editor {
        constructor() {
            this.content = null;
        }
    
        setContent(content) {
            this.content = content;
        }
    
        getContent() {
            return this.content;
        }
    
        saveContentToMemento() {
            return new Memento(this.content);
        }
    
        getContentFromMemento(memento) {
            this.content = memento.getContent();
        }
    }
    
    
    // 测试
    let editor = new Editor();
    let careTaker = new CareTaker();
    
    editor.setContent("111");
    editor.setContent("222");
    careTaker.add(editor.saveContentToMemento()); // 备份
    editor.setContent("333");
    careTaker.add(editor.saveContentToMemento()); // 备份
    editor.setContent("444");
    
    console.log(editor.getContent());
    editor.getContentFromMemento(careTaker.get(1)); // 撤销
    console.log(editor.getContent());
    editor.getContentFromMemento(careTaker.get(0)); // 撤销
    console.log(editor.getContent());
    

    13-10 中介者模式

    • 解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的 相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知 中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者
      模式使网状的多对多关系变成了相对简单的一对多关系(类似于观察者模式,但是单向的,由中介者统一管理。)
    /**
     * 中介者模式
     */
    
    class A {
        constructor() {
            this.number = 0;
        }
    
        setNumber(num, m) {
            this.number = num;
            if (m) {
                m.setB();
            }
        }
    }
    
    class B {
        constructor() {
            this.number = 0;
        }
    
        setNumber(num, m) {
            this.number = num;
            if (m) {
                m.setA();
            }
        }
    }
    
    class Mediator {
        constructor(a, b) {
            this.a = a;
            this.b = b;
        }
    
        setA() {
            let number = this.b.number;
            this.a.setNumber(number / 100);
        }
    
        setB() {
            let number = this.a.number;
            this.b.setNumber(number * 100);
        }
    }
    
    
    // 测试
    let a = new A();
    let b = new B();
    let m = new Mediator(a, b);
    a.setNumber(100, m);
    console.log(a.number, b.number);
    b.setNumber(100, m);
    console.log(a.number, b.number);
    

    13-11 访问者模式和解释器模式

    • 访问者模式: 给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
    class Context {
        constructor() {
          this._list = []; // 存放 终结符表达式
          this._sum = 0; // 存放 非终结符表达式(运算结果)
        }
      
        get sum() {
          return this._sum;
        }
        set sum(newValue) {
          this._sum = newValue;
        }
        add(expression) {
          this._list.push(expression);
        }
        get list() {
          return [...this._list];
        }
      }
      
      class PlusExpression {
        interpret(context) {
          if (!(context instanceof Context)) {
            throw new Error("TypeError");
          }
          context.sum = ++context.sum;
        }
      }
      class MinusExpression {
        interpret(context) {
          if (!(context instanceof Context)) {
            throw new Error("TypeError");
          }
          context.sum = --context.sum;
        }
      }
      
      /** 以下是测试代码 **/
      const context = new Context();
      
      // 依次添加: 加法 | 加法 | 减法 表达式
      context.add(new PlusExpression());
      context.add(new PlusExpression());
      context.add(new MinusExpression());
      
      // 依次执行: 加法 | 加法 | 减法 表达式
      context.list.forEach(expression => expression.interpret(context));
      console.log(context.sum);
    
    
    

    13-12 关于面试和日常使用

    第14章 综合应用

    14-1 综合应用-介绍和演示

    14-2 综合应用-UML类图分析

    14-3 综合应用-画UML类图

    14-4 综合应用-代码基础环境

    14-5 综合应用-实现List组件

    14-6 综合应用-实现item&cart

    14-7 综合应用-状态模式实现购物车操作

    14-8 综合应用-实现购物车列表和折扣

    14-9 综合应用-实现日志

    14-10 综合应用-总结

    第15章 课程总结

  • 相关阅读:
    深析Tomcat容器工作流程
    GC基础
    oracle sql总结
    mybatis返回值类型总结
    eclipse启动javaweb项目后,如何让局域网内的其他人访问
    oracle sql 逗号 分割 REGEXP_SUBSTR 函数
    Oracle12c安装时报"指定的oracle主目录用户已存在"错误
    spring
    Oracle sql where后面的多条件case when判断执行
    Oracle12c安装时报ins-30131
  • 原文地址:https://www.cnblogs.com/xulonglong/p/js-she-ji-mo-shi-dai-zheng-li.html
Copyright © 2011-2022 走看看