“这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战”
前言
在面试高级前端时,往往会遇到一些关于设计模式的问题,每次都回答不太理想。恰逢8月更文挑战的活动,准备用一个月时间好好理一下关于设计模式方面的知识点,给自己增加点面试的底气。
什么是命令模式
用一个生活例子来介绍。假如你是一家餐厅的服务员,那么你的工作应该是这样的:
当某位客人点餐后,你要根据客人点的菜在电脑上创建一条订单,创建成功后,这条订单进入订单列表中,厨房会收到新订单提醒开始做菜,客人不用关心是那位厨师帮他炒菜,只要在桌子上等菜就行。
当某位客人打电话订餐,要求一个小时后开始炒他的菜,你要根据客人点的菜在电脑上创建一条订单,并注明一个小时后开始炒,厨房会在一个小时后收到新订单提醒开始做菜。假如过了半个小时,客人有事来不了,打电话过来取消了,你要在订单列表找到这个客人的订单,取消订单即可,如果超过一个小时,就不能取消订单了。
如果有太多的客人点餐,厨房可以按照订单列表中的订单顺序排队炒菜。
上面就是一种命令模式,客人到餐厅吃饭,本质上是客人向厨师发起请求,厨师接收到请求后开始炒菜,但是客人不认识厨师,怎么办呢?餐厅就靠订单列表把客人和厨师关联起来,通过订单列表,客人就可以命令厨师开始炒菜,这些记录着订餐信息的订单列表,便是命令模式中的命令对象。
命令模式的用途
命令模式中的命令(command)指的是一个执行某些特定事情的指令。其最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
拿订餐来说,客人需要向厨师发送请求,但是客人完全不知道这些厨师的名字和联系方式。命令模式把客人订餐的请求封装成command
对象,也就是订餐中的订单列表对象。这个对象可以在程序中被四处传递,就像订单列表可以从服务员手中传到厨师的手中。这样一来,客人就不需要知道厨师的名字,从而解开了请求调用者和请求接收者之间的耦合关系。
另外,相对于过程化的请求调用,command
对象拥有更长的生命周期。对象的生命周期是跟初始请求无关的,因为这个请求已经被封装在了command
对象的方法中,成为了这个对象的行为。我们可以在程序运行的任意时刻去调用这个方法,就像厨师可以在客人预定一个小时之后才帮他炒菜,相当于程序在一个小时之后才开始执行command
对象的方法。除此之外,命令模式还支持撤销、排队等操作。
实践命令模式
假设正在开放一个餐厅订餐系统的界面,该界面有非常多个 Button 按钮,且这些按钮都用权限来控制。因为权限控制比较复杂,所以决定让一个程序员专门负责绘制这些按钮,而另外一个程序员则负责编写点击按钮后的具体行为,且这些行为都将被封装在对象里。
对于绘制按钮的程序员来说,他完全不知道某个按钮未来将用来做什么,可能用来刷新订单列表,也可能用来增加订单,他只知道点击这个按钮会发生某些事情。那么当完成这个按钮的绘制之后,应该如何给它绑定 onclick 事件呢?或许你很快就给出解决方案了。
<body>
<button id="button1">点击按钮 1</button>
<button id="button2">点击按钮 2</button>
<button id="button3">点击按钮 3</button>
</body>
<script>
const button1 = document.getElementById( 'button1' ),
const button2 = document.getElementById( 'button2' ),
const button3 = document.getElementById( 'button3' );
</script>
const bindClick = (button,func) =>{
button.onclick = func;
};
const OrderList = {
refresh(){
console.log( '刷新订单列表' );
},
};
const Order = {
add(){
console.log( '增加订单' );
},
del(){
console.log( '删除订单' );
}
};
bindClick( button1, OrderList.refresh);
bindClick( button2, Order.add);
bindClick( button3, Order.del);
上面的代码的确可以解决上述的需求,但是发送请求者bindClick( button1, Order.refresh)
和请求接收者Order
耦合在一起了,只要修改了Order
对象的方法名,发送请求者也得跟着修改。
回顾一下上文介绍的命令模式的用途,我们可以使用命令模式来实现上述需求,就可以消除请求发送者和请求接收者之间的耦合关系。
构建命令对象
如何构建命令对象command
是实现命令模式的关键。设计模式的主题总是把不变的事物和变化的事物分离开来,命令模式也不例外。所以在上述需求中,点击按钮后,会固定执行命令对象command
的一个方法execute
,这是不变的事物,而变化的事物是execute
方法中执行接收者receiver
中某个方法。
const command = (receiver) =>{
return {
execute(){
//执行接收者receiver中某个方法
}
}
}
接下来实现上面点击按钮2后会添加订单的功能。
const OrderCommand = (receiver) =>{
return {
execute: function () {
receiver.add();
}
}
};
const setCommand = (button, command) =>{
button.onclick = () =>{
command.execute();
}
};
const orderCommand = OrderCommand(Order);
setCommand(button2, orderCommand);
上述代码中OrderCommand
是用来创建一个命令对象的函数,setCommand
是给按钮上安装命令对象command
,点击按钮后就会执行命令对象command
中的execute
方法。
执行const orderCommand = OrderCommand(Order)
用Order
这个订单行为集合对象创建一个命令对象。
此时点击按钮就会执行Order
对象中的添加订单的方法add
,假如点击按钮不执行添加订单,而是要执行删除订单,只要修改OrderCommand
函数即可
const OrderCommand = (receiver) =>{
return {
execute: function () {
receiver.del();
}
}
};
而不要去同时修改发送请求者和接收请求者的代码,这就达到消除发送请求者和接收请求者之间的耦合关系。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!