设计模式
设计模式
# 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进行更新。