编程范式(Programming paradigm)指计算机编程的基本风格或典型模式。html
编程范型提供了(同时决定了)程序员对程序执行的见解。例如,在面向对象编程中,程序员认为程序是一系列相互做用的对象,而在函数式编程中一个程序会被看做是一个无状态的函数计算的序列。前端
着眼于解决问题的不一样方式,编程范式现存许多种,其中如:面向过程、面向对象、函数式编程等范式,咱们对此比较熟悉,他们也常常出如今咱们的视野中。为了进一步加深对编程范式的认识,这里介绍几种经常使用的编程范式。程序员
面向过程编程,也被称之为命令式编程,是一种最原始,也是咱们最熟悉,平常工做中使用较多的一种编程范式。从本质上讲,它是「冯.诺伊曼机」运行机制的抽象,它的编程思惟方式源于计算机指令的顺序排列。算法
面向过程的核心是将解决问题的步骤分析出来,用函数将这些步骤实现,而后再被依次调用。编程
好比一个经典的例子:后端
把大象放在冰箱分为几步?微信
第一步:把冰箱打开ide
第二步:把大象装进去函数式编程
第三步:把冰箱门关上函数
得益于面向过程的直观性,以及最接近于程序的实际运行,到如今,它仍然被大范围的使用。
可是它不适合某类问题的解决,例如那些非结构化的具备复杂算法的问题。它必须对一个算法加以详尽的说明,而且其中还要包括执行这些指令或语句的顺序。实际上,给那些非结构化的具备复杂算法的问题给出详尽的算法是极其困难的;而且它强调顺序相当重要,而这并不适合全部问题。
面向对象编程(OOP:Object Oriented Programming)把对象做为程序的基本单元,一个对象包含了数据和操做数据的函数,它最先由 Alan Kay 在 1966 年或 1967 年在研究生期间提出。
与面向过程编程不一样,在面向过程编程中,数据和处理数据的函数彼此独立,咱们须要先将数据处理成函数能接受的格式,而后调用相关函数。而在在面向对象中,数据和处理数据的函数都在一个类中,经过初始化实例传递数据。
现现在,当谈及面向对象时,下意识的就会联想出它的三个特性:封装、继承与多态。
class Animal {
constructor( public name: string ) {}
eat() {
console.log('eat food')
}
}
class Cat extends Animal {
constructor() {
super('Cat')
}
}
class Bird extends Animal {
constructor() {
super('Dog')
}
}
复制代码
网上也有一张的图,形象的解释了面向对象中的一些常见概念:
用面向对象的方式来解释「把大象关进冰箱」:
冰箱.开门()
冰箱.放入(大象)
冰箱.关门()
面向切面编程(AOP:Aspect Oriented Program)是对面向对象编程的一个补充。咱们知道在面向对象编程时,没法作到重复使用与主业务无关的某些方法。好比 A、B 两个类,若是都要使用日志功能,按照面向对象的设计原则,你必须在每一个类里面都写上这样一个方法,尽管他们几乎一摸同样。固然你也能够选择将这样一段代码放到一个独立的类里,而后再在A、B类中调用,可是这样就等于造成了一个强耦合的关系,它的改变会影响到这两个类。面向切面编程,正是解决的这样一个问题:把和主业务无关的事情,放到代码外面去作。
好比在前端,实现一个代码埋单时:
若是不使用 AOP 你的代码多是这样:
class Test {
add(paras: Params) {
// 埋点代码
// 其余代码
}
}
复制代码
可是这样会形成耦合,且不能复用。
使用 AOP 时:
function log(): MethodDecorator {
return (target, key, descriptor) => {
// 在这里你能够加上你的埋点接口
// 或者作一些其余事情
return descriptor;
};
}
class Test {
@log()
add(paras: Params) {
//
}
}
复制代码
再好比后端接口的鉴权,你可能会在中间件中处理:
function guardsMiddle(req, res, next) {
// 这里可能会有各类判断
// 判断 url 是不是指定 url,而后再判断是否有权限
next()
}
复制代码
或者你会在某个具体的 controller/services 中处理:
class SomeController {
getUserInfo() {
// 现判断是否有对应的权限
// 再作具体的处理
}
}
复制代码
在 AOP 中,则多是这样:
@Controller()
class SomeController {
@Get('userInfo')
@Roles('admin')
getUserInfo() {
//
}
}
复制代码
同面向切面编程同样,面向接口也是对面向对象的一种补充。它规定了实现本接口的类必须拥有的一组规则。好比:
interface Animal {
eat(): void;
}
class Cat implements Animal {
eat() {}
}
class Dog implements Animal {
eat() {}
}
复制代码
在开发 vscode 插件时,会使用不少从 vscode 中暴露出来的接口:
class StatusBar implements vscode.Disposable {
dispose() {
// xxx
}
}
class TargetTreeProvider implements vscode.TreeDataProvider<{}> {
getChildren() {
// xxx
}
}
复制代码
你可能会有疑问,我也可使用抽象类实现,那么区别是什么?
abstract class Animal {
abstract eat(): void;
}
class Cat extends Animal {
eat() {}
}
复制代码
在文中这两个例子里,使用接口或是抽象类,是没有多少区别的,惟一的区别仅仅是在 TypeScript 编译成 JavaScript 后,interface 不存在了(不会有额外使用额外空间)。除此以外,抽象类能够包含程序的实现细节,便是能够实现代码的复用。
abstract class Animal {
constructor(private name: string) {}
abstract eat(): void;
printName() {
console.log(this.name);
}
}
class Cat extends Animal {
constructor() {
super('cat');
}
eat() {}
}
class Dog extends Animal {
constructor() {
super('dog');
}
eat() {}
}
复制代码
另外,接口和抽象类的另一个区别在于:抽象类和它的子类之间应该是通常和特殊的关系,而接口仅仅是类应该实现的一组规则。
现在,编程范式现存许多种:
每一个编程范式在本身所注重的场景里发挥着举足轻重的做用。好比 OOP 注重的是数据和行为的打包封装以及程序的接口和实现的解耦;而 FP 最大的特色是纯函数的无状态性质;面向过程则更贴近于实际工程中硬件的运行方式。
每一个范式都有它的「灵魂」,只有在实际使用时,才能理解。
在实际项目中,更多的时候,咱们是使用的多范式编程,正如范·罗伊信仰的同样:解决一个编程问题,须要选择正确的概念;解决多个问题,则须要组合分属不一样部分的多个概念。
更多文章,请关注公众号: