设计模式

设计模式

# 1.单例模式

单例模式的核心思想是,将一个实例作为全局复用的实例。全局只有一个实例,数据共享。常见案例:全局状态vuex,Jquery中的全局对象$,浏览器中的window、document 都算是单例。

# 方法1:全局对象

将实例挂载到全局对象上,后面使用直接window.xxx

# 方法2:将实例挂载到构造函数上

给构造函数增加一个方法,判断是否存在Instance,如果不存在则new一个实例挂载到原型上,如果存在则返回这个实例。 然后使用的时候直接调这个原型方法

function GlobalUser(name) {
  this.name = name
  this.id = 1002
}
// 基于构造函数的静态函数作为统一入口,Constructor.getInstance()
GlobalUser.getInstance = function(name) {
  // 注意这里的this指向的是构造函数GlobalUser
  if (this.instance) return this.instance
  // 第一次没有创建
  return this.instance = new GlobalUser(name)
}

# 方法3:闭包-new

用闭包保存唯一的实例对象,如果对象有值就返回这个对象,如果没有就新建一个 然后每次new出来的其实就是同一个实例了

let GlobalUser = (function() {
  let instance  // 闭包保存的唯一实例对象
  return function(name) {
    if (instance) return instance
    // (首次)创建实例
    instance = { name: '张三', id: 1003 }
    return instance
  }
})()  // 立即执行,外层函数的价值就是他的闭包变量instance
console.log(new GlobalUser('张三').name)   // 张三
console.log(new GlobalUser('李四').name)   // 张三,依然是张三,复用了第一次创建的实例 
console.log(new GlobalUser() === new GlobalUser())  // true

# 方法4:ES6模块Module

ES6的模块其实就是单例模式,模块中导出的对象就是单例的,多次导入其实是同一个引用。 直接改模块里的变量就行

# 实际操作

以map.js为例。

声明一个class,里面是各种方法、属性, 然后直接new出一个实例。export default导出这个实例。 然后再项目中用是时候,就 import map from './map'; map.XXX; 这样就可以是全局都使用同一个Map实例了。 原理是先new出一个实例,利用ES6模块Module导出这个实例,多次导入其实是同一个。这样全局就能全局唯一了

// map.js

let plotInstance = null;
let picking = null;
let RouteLayer;
let _store;
const callbackMap = {};

export class Map {
    setMapInstance(mapInstance, store) {
        this.mapInstance = mapInstance;
        _store = store;
    }

    getMapInstance() {
        return this.mapInstance;
    }

    // 设置视角
    setView(lng, lat, zoom) {
        const mapInstance = this.getMapInstance();
        const initZoom = mapInstance.getZoom();
        const newZoom = zoom || initZoom;
        if (!lng || !lat) {
            console.error(`经纬度数据错误, lng:${lng}, lat:${lat}`);
            return;
        }
        mapInstance.centerAndZoom([lng, lat], newZoom);
    }

    changeLayerType(type){
        EVENT_BUS.emit(CHANGE_LAYER_TYPE, type);
    }

    // 添加点
    addMarkers(data, callback) {
        const {
            id, dataSources, noNewLayer, zIndex = 1,
        } = data;
        const layers = this.mapInstance.getLayers().array_;
        console.log(layers);
        let layer = layers.find(v => v.values_.name === id);
        if (layer) {
            if (!noNewLayer) {
                this.mapInstance.removeLayer(layer);
                layer = new QMap.VectorLayer({
                    name: id,
                    zIndex,
                });

                this.mapInstance.addLayer(layer);
            }
        } else {
            layer = new QMap.VectorLayer({
                name: id,
                zIndex,
            });

            this.mapInstance.addLayer(layer);
        }
        const feaArr = [];
        dataSources.forEach(item => {
            if (item?.position?.length && item.position[0] && item.position[1]) {
                const m = new QMap.Marker(item);
                if (item.id) {
                    m.setId(item.id);
                }
                if (callback) {
                    m.on('click', callback);
                }
                feaArr.push(m);
            }
        });
        layer.addFeatures(feaArr);
        return layer;
    }

    getMarkerById(layerId, markerId) {
        const layer = this.getLayerById(layerId);
        if (layer) {
            return layer._source.idIndex_[markerId.toString()];
        }
        return null;
    }

    // 两点之间距离
    getDistance(start, end) {
        const A = QMap.Convertor.LL2MC(start[0], start[1]);
        const B = QMap.Convertor.LL2MC(end[0], end[1]);
        const distance = Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
        return distance.toFixed(2);
    }

    measure(type, flag) {
        const measure = new QMap.Measure({
            map: this.mapInstance,
        });
        if (flag) {
            measure.turnOn(type);
        } else {
            measure.turnOff();
        }
    }
}

export default new Map();

# 2.适配器模式

  • 定义:适配器模式是解决两个类之间的方法不兼容的问题。使用适配器模式之后,原本由于方法不兼容而不能工作的两个类可以一起工作。 适配器模式是一个相对简单的模式。在程序开发中有许多这样的场景:当我们试图调用模块或者对象的某个方法时,却发现这个方法的格式并不符合目前的需求。 这时候有两种解决办法,第一种是修改原来的方法实现,但如果原来的模块很复杂,或者我们拿到的模块是一段别人编写的经过压缩的代码,修改原方法就显得不太现实了。第二种办法是创建一个适配器,将原方法转换为客户希望的另一个方法,客户只需要和适配器打交道。

说人话:就是如果用到两种不同的api ,功能类似但不兼容,就中间封装一层做个转换。 ————antmove就是适配器模式 或者是处理两份不同格式的数据,进行转换成相同的 实际项目:产品的融合通信功能,本身不是对接的华为,但需要做的业务差不多, 对接华为融合通信,相当于重新对了套接口

# 3.策略模式

  • 定义:策略模式(Strategy Pattern)指的是定义一系列的算法,把它们一个个封装起来,目的就是将算法的使用与算法的实现分离开来。即解耦。通常就是getXX().将需要计算的单独提到一个方法里

  • 常见场景:if-else。不同的枚举值可以做个映射

const getCouponText = (type) => {
    if (type === 1) {
        return '免费券'
    } else if (type === 2) {
        return '立减券'
    } else if (type === 3) {
        return '折扣券'
    }
    ...
}

修改成

const COUPON_TYPES = {
    FREE: '免费券',
    DISCOUNT: '折扣券',
    REDUCE: '立减券'
}
const getCouponText = (type) => {
    return COUPON_TYPES[type]||''
}

# 观察者模式——也可以交发布订阅

观察者模式是一种对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。VUE的双向绑定就是通过这个模式。 就是创建一个被观察者,在vue中就是dep,将所有依赖他的对象收集到被观察者中,也就是watcher。当dep改变时,通知所有的dep进行更新。

框架部分

框架部分

框架 部分

vue 系列 https://juejin.cn/post/6961222829979697165?utm_source=gold_browser_extension