前端设计模式

策略模式

定义一系列的算法,把它们一个个封闭起来,并且使他们可以相互替换

举例:处理商店促销类型

prePrice - 处理预热价
onSalePrice - 处理大促价
backPrice - 处理返场价
freshPrice - 处理尝鲜价
askPrice - 分发询价逻辑

实现:原本全部放在askPrice,并通过ifelse分支处理每种情况, 改为封装每一个单独功能
但还是存在缺点:没有实现“对扩展开放,对修改封闭”的效果。

// 处理预热价
function prePrice(originPrice) {
  if(originPrice >= 100) {
    return originPrice - 20
  } 
  return originPrice * 0.9
}

// 处理大促价
function onSalePrice(originPrice) {
  if(originPrice >= 100) {
    return originPrice - 30
  } 
  return originPrice * 0.8
}

// 处理返场价
function backPrice(originPrice) {
  if(originPrice >= 200) {
    return originPrice - 50
  }
  return originPrice
}

// 处理尝鲜价
function freshPrice(originPrice) {
  return originPrice * 0.5
}

function askPrice(tag, originPrice) {
  // 处理预热价
  if(tag === 'pre') {
    return prePrice(originPrice)
  }
  // 处理大促价
  if(tag === 'onSale') {
    return onSalePrice(originPrice)
  }

  // 处理返场价
  if(tag === 'back') {
    return backPrice(originPrice)
  }

  // 处理尝鲜价
  if(tag === 'fresh') {
     return freshPrice(originPrice)
  }
}

策略模式实现:对象映射

// 询价函数
function askPrice(tag, originPrice) {
  return priceProcessor[tag](originPrice)
}
// 定义一个询价处理器对象
const priceProcessor = {
  pre(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 20;
    }
    return originPrice * 0.9;
  },
  onSale(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 30;
    }
    return originPrice * 0.8;
  },
  back(originPrice) {
    if (originPrice >= 200) {
      return originPrice - 50;
    }
    return originPrice;
  },
  fresh(originPrice) {
    return originPrice * 0.5;
  },
};

加入添加一个新人价格

priceProcessor.newUser = function (originPrice) {
  if (originPrice >= 100) {
    return originPrice - 50;
  }
  return originPrice;
}

总结定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,负责具体的计算过程。第二个部分是环境类context,comtext接受客户的请求,随后把请求委托给某一个策略类。

算法,就是我们这个场景中的询价逻辑,它也可以是你任何一个功能函数的逻辑;“封装”就是把某一功能点对应的逻辑给提出来;“可替换”建立在封装的基础上,只是说这个“替换”的判断过程,咱们不能直接怼 if-else,而要考虑更优的映射方案。

代理模式

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的访问

代理模式在前端中比较常用的虚拟代理和缓存代理。

为什么要使用代理模式
中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类

保护代理:
用于控制不同权限的对象对目标对象的访问,但是在js并不容易实现保护代理,因为我们无法判断谁访问了某个代理。

虚拟代理:

  1. 虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。
  2. 虚拟代理的特点是,代理类和真正的类都暴露了同样的接口,这样对于调用者来说是无感的。

提到虚拟代理,其最具代表性的例子就是图片预加载。预加载主要是为了避免网络延迟、或者图片太大引起页面长时间留白的问题。通常的解决方案是先给 img 标签展示一个占位图,然后创建一个 Image 实例,让这个实例的 src 指向真实的目标图片地址,当其真实图片加载完成之后,再将 DOM 上的 img 标签的 src 属性指向真实图片地址。

class MyImg {
	static imgNode = document.createElement("img")
	constructor(selector) {
    	selector.appendChild(this.imgNode);
    }
    
    setSrc(src) {
    	this.imgNode = src
    }
}

class ProxyMyImg {
	static src = 'xxx本地预览图地址loading.gif'
	constructor(selector) {
		this.img = new Image
        this.myImg = new MyImg(selector)
        this.myImg.setSrc(this.src)
    }
    
    setSrc(src) {
    	this.img.src = src
    	this.img.onload = () => {
    		this.myImg.setSrc(src)
        }
    }
}

const img = new ProxyMyImg(document.body)
img.setSrc('xxx')


缓存代理:
缓存代理可以为一些开销大的运算结果提供暂时的缓存,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回缓存好的运算结果。

const mult = (...args) => {
	console.log('multing...')
    let res = 1
    args.forEach(item => {
    	res*=item
    })
    return res 
}
const proxyMult = (() => {
	const cache = {}
    return (...args) => {
    	const key = [].join.call(args, ',')
        if (key in cache) {
        	return cache[args]
        }
        return cache[key] = mult.apply(null, args)
    }
})()
proxyMult(1,2,3,4)// multing... 24
proxyMult(1,2,3,4)//24

Proxy:ES6新增了代理类Proxy
语法
const p = new Proxy(target, handler)

  • target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  • handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
const mult = (args) => {
	console.log('multing...')
    let res = 1
    args.forEach(item => {
    	res*=item
    })
    return res
}

const handler = {
    cache: {},
    apply: function(target, thisArg, args) {
        const key = [].join.call(args, ',')
        if(key in this.cache) {
            return this.cache[key]
        }
        return this.cache[key] = target(args)
    }
}
const proxyMult = new Proxy(mult, handler)
proxyMult(1,2,3,4)
//multing...
//24
proxyMult(1,2,3,4)
//24


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值