编程的一些抽象核心
一、程序、算法
本质
程序=算法+数结结构
算法=逻辑+控制
程序=算法+数结结构,这个是众所周知了,算法这个解释则比较清丽脱俗:1.逻辑 2. 控制。
- 逻辑用来解决实际的问题, 控制决定用什么策略来解决问题,逻辑是真正意义上的解决问题的算法
- 控制是一个程序流转的方式,即程序执行的方式,并行还是串行,同步还是异步,调度不同执行路径或模块,数据之间的存储关系。
逻辑是是处理什么
,控制是怎么做
,沟通方式是数据结构
举例
表单验证经常的做法是
function check(data) {
名字 = get(data, 名字)
if (名字为空 || 名字长度小于3) {
return 验证失败,名字非法
}
密码 = get(data,密码)
if (密码为空 || 密码长度小于8) {
return 验证失败,密码非法
}
邮箱 = get(data,邮箱)
if (邮箱为空 || 邮箱格式不正确) {
return 验证失败,邮箱非法
}
...
return 验证通过;
}
这个函数最终逻辑是验证输入数据的合法性,但是这样其实就是把控制和逻辑耦合在一起了,随着验证字段的增多,这个函数会越来越长,最终造成可读性越来越差,越来越难维护
更好的分离两者的方法是,定义一个表单验证器,如下
var rules = {
名字: ["长度大于3"],
密码: ["长度大于8"]
邮箱: ["email格式"]
...
]
};
if 失败结果=checkRules(rules).执行(); 失败结果不为空 {
return 验证失败,失败结果
}
return 验证通过
这样通过描述验证规则,得到了一种控制和逻辑之间的解耦,不用再在check里边各种if else了。
绝大多数程序复杂混乱的根本原因是业务逻辑和控制逻辑的耦合
二、面向对象、设计模式
面向对象、设计模式的重点就是:
- 使用接口抽象了具体的实现类,这样其它类耦合的是接口而不是实现类,这就是多太,增加了程序的扩展性
- 接口也就是一种协议,就像HTTP协议一样,浏览器和后端的程序都依赖于这一种协议,而不是具体的实现(如果是依赖具体实现,那么浏览器就要依赖后端的编程语言或中间件了,这就太恶心了)。于是,浏览器和后端的程序就完全解除依赖关系,而去依赖一个标准的协议了,这也是Ioc/DIP的本质。
23个设计模式基本就是说了两个面向对象的核心理念:面向接口编程、组合优于继承
面向接口编程
- 使用者不需要知道数据类型、结构、算法的细节
- 使用者不需要知道实现细节,只需要知道提供的接口
组合优于继承
继承需要给子类暴露一些父类的设计和实现细节,父类实现的改变会造成子类也需要改变,而组合则不存在这样的弊端。
我们以为继承主要是为了代码重用,但实际上在子类中需要重新实现很多父类的方法,继承更多的应该是为了多态。
三、依赖倒置和控制反转 (IoC/DIP)
简单说就是要依赖于抽象接口,不要依赖于具体实现
这就是控制反转,开关从以前设备的专用开关,转变到了控制电源的开关,而以前的设备要反过来依赖开关厂声明的电源接口
钱
从以前的以物易物,变成了依赖“钱”,所有的商品都依赖这个“钱”的交易协议,不用再互相依赖了,整个世界的动作就简单了很多
开关和灯
有一个开关要控制一个灯的开和关,最直接的方法就是把开关和灯做到一起。
但是,如果有一天,我们发现这个开关可能还要控制别的不仅是灯泡的东西,就会发现开关耦合了灯泡这种类别,非常不得扩展。
解决方案就是,造开关工厂根据不需要关心要控制的东西是什么,只做一个开关,功能就是把电接通和断开,不管是手动的、声控的还是光控、遥控的,开关厂和电灯厂依赖于一个标准的通电和断电的接口。
造灯泡的工厂也不关心你用什么样的开关,只管把灯的电源接口做出来,开关厂和电灯厂依赖于一个标准通电和断电的接口。
证券交易所
在交易的过程中,卖家向买家卖东西,一手交钱一手交货,基本是要见面的,这个时候银行出来做担保,买家先把钱打到银行,银行让卖家发货,买家验货后,银行再把钱打给卖家。
这就是反转控制,买卖双方把对对方的直接依赖和控制,反转到了让对方来依赖一个标准的交易模型的接口。