三明治代码

三明治代码是代码复用的一种方案,或者一种思路

问题的产生

假如,现有一个需求,“去超市买一瓶矿泉水”,代码可能像这样:

  1. 走近超市
  2. 在入口处,检测体温(新冠啊,新冠!)
  3. 进入购物区
  4. 去找到一瓶矿泉水,并放到购物车里
  5. 去收银台接算
  6. 走出超市

过了几天,来了新需求,“去超市买一袋薯片”
那么把上面的代码,复制、粘贴,再把第 4 步改一下,就可以直接用了

那么,这时候,就产生了重复代码

小试牛刀

这个很容易解决:

封装个函数嘛,把要买的东西作为参数,传进去
function 去超市买东西(要买的东西) {
  走近超市();
  检测体温();
  进入购物区();
  加入购物车(要买的东西);
  去收银台接算();
  走出超市();
}

这很棒,用简单的方法,解决简单的问题

现实是非常复杂的

但现实问题往往没那么简单
我记得小时候,有的超市里是有小餐厅的,凉皮、卤菜……
点了想吃的东西,当场就需要付钱(不需要到出口处的收银台)
做好了立刻可以吃(不必在超市外吃)

那么,可能就会有这样的需求:

去超市吃个凉皮

显然,刚刚写的函数不好用了
但是,做些改动,就又行了:

function 去超市买东西(要买的东西, 要现场吃的东西) {
  走近超市();
  检测体温();
  进入购物区();
  if(要买的东西)
    加入购物车(要买的东西);
  if(要现场吃的东西) {
    去小餐厅();
    点餐(要现场吃的东西);
    结算();
    等餐();
    ();
  }
  去收银台结算();
  走出超市();
}

是的,又行了,但不太好
因为,可能还有其他种类的服务,不适用这个函数
比如:

  • 有可能要买衣服,而买衣服需要试穿一下
  • 不同品牌的商品,不需要比对一下吗?不同类型的商品,其比较的“算法”一样吗?
  • 超市里可能有小朋友的游玩区,家长在买东西的时候,可以把小朋友寄存在那里(当然,你可能没小孩,但有可能你自己想玩啊)
  • ……

可以根据不同的需求,不断扩充上面的函数
但最后,它可能变成一个很大的函数,既丑陋,又不好维护

英雄登场

可以把在超市进行的操作,封装到一个函数里
然后把这个函数,作为参数,传给“去超市”函数

function 去超市(要做的事) {
  走近超市();
  检测体温();
  进入购物区();
  要做的事();
  去收银台接算();
  走出超市();
}
function 买条裤子() {
  找一条喜欢的裤子();
  试穿各种型号();
  把合适的型号放入购物车();
  在_服装区收银台_接算();
  剪裤脚();
}
去超市(买条裤子);

不错吧~

总结

为什么叫“三明治代码”?
因为三明治,往往是上下两片面包,中间可能是煎蛋、可能是煎牛排、可能是蔬菜……
甚至是两片面包夹个三明治
上下不变(永远是面包),夹心,则需要发挥想象力,谁说没有臭豆腐夹心的三明治?

再回头看看上面的 去超市函数:

function 去超市(要做的事) {
  走近超市();
  检测体温();
  进入购物区();
  // 上面三行,相当于三明治的“上层的面包” 要做的事();
  // 夹心,千变万化,你甚至可以写一个“啥也不买,就看看”的函数
  // 这下面,就相当于三明治的“下层面包”了
  去收银台接算();
  走出超市();
}

英雄返场

上面说的夹个三明治可不是开玩笑
比如,需求是这样的:

周末的时候,你在家,要去超市买一袋薯片
  • 出门,下楼,走出小区
  • 走向超市
  • 去超市(买一袋薯片)
  • 走回家
  • 在小区门口测体温
  • 进小区,上楼,进门

这时候,“去超市()”,就成了夹心
或者,你在家,不是去超市,而是去剪头发、菜市场……换换夹心就行了
或者,你在公司,下午,饥肠辘辘,需要去楼下的小超市买个…… 那就需要换面包了
或者,你在公司,要回家拿个东西
或者,你在家,要去公司拿个东西
……

没有绝对的面包或夹心
你可以用全麦面包夹煎蛋
也可以用荞麦面包夹生菜
也可以用两片全麦面包夹一片荞麦面包
也可以用两片煎牛排夹一片荞麦面包(肉夹馍?)
或者用两片荞麦面包夹上面的任意一种三明治

编辑于 2021-11-17 09:52