前置说明:这是我自创的名词,网上找不到
一线法编程的起源
程序员编码的最大问题是受制于最初 c++面向过程的思维引导,导致后续所有的代码里面,if...elseif...非常多,同时想要让自己的一串代码尽可能处理更多可能的场景,此外,还想将公共逻辑抽出,我们最喜欢做的事情就是,先设置几个变量,然后不断地分析逻辑,不断地追加变量的值,然后下面不知道哪个逻辑开始判断赋值,逻辑死死分不开,后来人没办法,就得看上百行代码,然后再在这团恶心的代码上,先理清逻辑,最后只能在下面继续追加 if 判断逻辑,随着变量的增多,学过排列组合的都知道,下面追加的代码的 if 组合情况就会层层嵌套,达到阶乘级逻辑判断,cpu 大多数时间都在左顾右盼、跑来跑去,人脑也在这种逻辑中,搅合得像一团浆糊,这样的代码不断堆叠,最终将一个项目推向重构和死亡。
let isLogin = 0;
let isUser = 0;
let isAdmin = 0;
xxxxxx
xxxxx
xxxx
isLogin = 2;
if(isLogin){
xxxxxx
}elseif(isLogin===2){
isAdmin = 1
xxxxxx
}elseif (isLogin===3){
xxxxxxx
}else{
isUser = 1
}
if (isUser === 3 && isAdmin ===1 ){
xxxxxx
}
但是如果我们这样写写看, 你觉得可维护性如何?如何反射机制一直搞下去,是不是兼容性更强?下次再有一种 IOSLogin,我们是不是就只需要加一个 IOSLogin,下次再有一种 VIP3User,我们是不是直接塞个类+函数,然后重写一个 deal()函数放在指定目录下?
新来的开发者,还会骂爹?
let loginTypes = ['AppLogin', 'WebLogin', 'DeskLogin', 'MinigromLogin']
let userTypes = ['CommonUser', 'VIP1User', 'VIP2User', 'AgentUser']
let adminTypes = ['StoreAdmin','SellerAdmin', 'SuperAdmin']
let loginType = req.params['loginType']
let userType = req.params['userType']
let adminType = req.prams['adminType']
// 获取类的构造函数
const classConstructor = eval(loginType);
// 使用反射机制创建实例
const instance = Reflect.construct(classConstructor, ['Alice', 30]);
// 使用反射机制调用方法
const dealLoginLogic = Reflect.apply(instance.deal(), instance, []);
// 执行处理登录逻辑,这个逻辑到底是上面四种那种登录我们都不关心 但是返回的登录信息大
// 致相同 在若弱类型语言尤其方便
const loginedInfo = dealLoginLogic()
if (loginInfo.isNotAuth){
return res.end()
}
const userInfo = loadUserInfo(loginInfo, userType)
if (!userInfo.isAdmin){
return res.end()
}
const adminInfo = loadAdminInfo(userInfo, adminType)
return mergeInfo(userInfo, adminInfo)
一线法是什么?
上面给的一个案例已经很能说明问题,但是只是给出了一种形式,所谓一线法,应该不是这一种逻辑,我们可以通过 Map 的方式,将键为字符串,值为二维数组的方式,仍然通过反射达到更好的对应,或者我们可以通过 js 更强大的 Map,将键也为函数,值为二维数组的方式,达到更灵活的处理,无论我们用什么,反射机制,抽象类,接口,是不可能被我们所忽略的,而作为开发程序员来说,可能你们玩了 10 年之久也没用过这些东西,这是一个很大的遗憾,正说明你们没有完全理解这些东西的好处和存在意义。
一线法主要就是将交叉的逻辑先理出来一条线,完全走下去,通过反射机制,Map 机制,将 if 判断尽可能降低为 0,让原来 if 判断的逻辑全部都删除掉,一切全都直接用反射机制和 Map 机制一顺到底,在这条路上没有分叉点,没有逻辑判断,有的就是一条路通到底,这样你的思维逻辑才不会卡壳,只要你的单元测试过得去,你就可以很自信地跟别人说,我写的代码理论上不可能出问题。
那些重叠的逻辑怎么办?
众所周知,只要你是一个使用 intellij 高级 IDE 的人,都会注意到,一旦你的代码出现了重复逻辑,那么 IDE 就会立马提醒你封装重构,如果你是处女座或者有强迫症的开发人员,一旦看到这种报错或者标黄的代码,你就忍不住把这段重复的代码封装成独立的类或函数
代码的逻辑就像一张三维立体的网,每个交织点就是一个封装函数,但是网由线组成,于是你总能从网里抽出很多线来
这条线就是我说的一条线法
一条线法的好处
任何编码规范都是节省你的生命的,如果你是一个不太在意生命而且又比较邋遢的开发者,估计看完也没啥感受,但是我相信如果你不是,看完这种编码方式,你定会觉得眼前一亮。
一条线法才真正践行了可持续代码的可行性,为什么?
代码的解耦
网上太多东西在讲解耦,但是都是业务逻辑上的解耦,业务逻辑其实解不了多少耦,传入参数总会影响函数体,因此传入总会改变传出,所以即使你不停地写函数,还会有更多函数要写,因为业务不断变动,函数也要不断更改修正,所以业务不死,新函数就层出不穷,因此业务解耦是个伪命题。
一线法其实也没从实质上解耦,但是它可以让后来者和维护者只关注网的节点处,而替换这个节点,你只需要新增一个节点即可,你只要保证新增的节点,单元测试 OK,那么其他的逻辑你都不用关心了,这就是最大的解耦了!你的脑子里不需要理清一个大项目的来龙去脉,就可以写代码了,算是很大的解耦了。
一线法的核心:嵌入式思维非常实用!
例如谷歌插件开发中,想要在网页里面追加一个图标,这个图标要实现一堆复杂的弹窗功能,使用 new Vue 整理成一个大对象,然后绑定在创建的 div 上,再把这个 div appendTo 某个 element 上,整个过程打包为一个独立的 js 文件,然后在 content script 那边维护一个数组列表,循环注入即可,怎么保证注入的顺序,注入的时机,如何实现注入,新来的人都不需要关心,他只需知道如何把这个小图标以及图标的功能写好然后放在指定的文件夹下就 OK。