《架构整洁之道》(二)--编程范式

编程范式指的是程序的编写模式,与具体的编程语言关系相对较小。这些范式会告诉你应该在什么时候采用什么样的代码结构。直到今天,我们也一共只有三个编程范式,而且未来几乎不可能再出现新的。

一、结构化编程

结构化编程是第一个普遍被采用的编程范式,我们可以将结构化编程范式归结为一句话:结构化编程对程序控制权的直接转移进行了限制和规范。

结构化编程范式可将模块递归降解拆分为可推导的单元,这就意味着模块也可以按功能进行降解拆分。这样一来,我们就可以将一个大型问题拆分为一系列高级函数的组合,而这些高级函数各自又可以继续被拆分为一系列低级函数,如此无限递归。更重要的是,每个被拆分出来的函数也都可以用结构化编程范式来书写。

结构化编程范式中最有价值的地方就是,它赋予了我们创造可证伪程序单元的能力。这就是为什么现代编程语言一般不支持无限制的goto语句。更重要的是,这也是为什么在架构设计领域,功能性降解拆分仍然是最佳实践之一。无论在哪一个层面上,从最小的函数到最大组件,软件开发的过程都和科学研究非常类似,它们都是由证伪驱动的。软件架构师需要定义可以方便地进行证伪(测试)的模块、组件以及服务。为了达到这个目的,他们需要将类似结构化编程的限制方法应用在更高的层面上。

二、面向对象编程

我们也可以用一句话来总结面向对象编程:面向对象编程对程序控制权的间接转移进行了限制和规范。

任何一种支持面向对象的编程语言必须支持这三个特性:封装(encapsulation)、继承(inheritance)、多态(polymorphism)。

1、封装

通过采用封装特性,我们可以把一组相关联的数据和函数圈起来,使圈外面的代码只能看见部分函数,数据则完全不可见。

然而,这个特性其实并不是面向对象编程所独有的。其实,C语言也支持完整的封装,下面来看一个简单的C程序:

 上述C程序是很常见的。在头文件中进行数据结构以及函数定义的前置声明(forward declare),然后在程序文件中具体实现。程序文件中的具体实现细节对使用者来说是不可见的。对使用point.h的程序是没有Point结构体成员的访问权限的。它们只能调用makePoint()函数和distance()函数,但对它们来说,Point这个数据结构体的内部细节,以及函数的具体实现方式都是不可见的。

而C++作为一种面向对象编程语言,反而破坏了C的完美封装性。因为C++编译器要求类的成员变量必须在该类的头文件中声明。这样一来,我们的point.h程序随之就改成了这样:

point.h文件的使用者现在知道了Point的成员变量x和y的存在!虽然编译器会禁止对这两个变量的直接访问,但是使用者仍然知道了它们的存在。而且,如果x和y变量名称被改变了,point.cc也必须重新编译才行!这样的封装性显然是不完美的。

 由此可见,面向对象编程在应用上确实会要求程序员尽量避免破坏数据的封装性。但实际情况是,那些声称自己提供面向对象编程支持的编程语言,相对于C这种完美封装的语言而言,其封装性都被削弱了,而不是加强了。

2、继承

继承的主要作用是让我们可以在某个作用域内对外部定义的某一组变量与函数进行覆盖。这事实上也是C程序员早在面向对象编程语言发明之前就一直在做的事了。下面,看一下刚才的C程序point.h的扩展版:

请仔细观察上面的main函数,这里NamedPoint数据结构是被当作Point数据结构的一个衍生体来使用的。之所以可以这样做,是因为NamedPoint结构体的前两个成员的顺序与Point结构体的完全一致。简单来说,NamedPoint之所以可以被伪装成Point来使用,是因为NamedPoint是Point结构体的一个超集,同时两者共同成员的顺序也是一样的 

 上面这种编程方式虽然看上去有些投机取巧,但是在面向对象理论被提出之前,这已经很常见了。其实,C++内部就是这样实现单继承的。

3、多态

多态是面向对象编程的核心,面向对象编程就是以多态为手段来对源代码中的依赖关系进行控制的能力,这种能力让软件架构师可以构建出某种插件式架构,让高层策略性组件与底层实现性组件相分离,底层组件可以被编译成插件,实现独立于高层组件的开发和部署

三、函数式编程

我们在这里可以将函数式编程范式总结为下面这句话:函数式编程对程序中的赋值进行了限制和规范。

我们知道一切并发应用遇到的问题,一切由于使用多线程、多处理器而引起的问题,如果没有可变变量的话都不可能发生。

一种常见方式是将应用程序,或者是应用程序的内部服务进行切分,划分为可变的和不可变的两种组件。不可变组件用纯函数的方式来执行任务,期间不更改任何状态。这些不可变的组件将通过与一个或多个非函数式组件通信的方式来修改变量状态。

由于状态的修改会导致一系列并发问题的产生,所以我们通常会采用某种事务型内存来保护可变变量,避免同步更新和竞争状态的发生。事务型内存基本上与数据库保护磁盘数据的方式类似,通常采用的是事务或者重试机制。

这里的要点是:一个架构设计良好的应用程序应该将状态修改的部分和不需要修改状态的部分隔离成单独的组件,然后用合适的机制来保护可变量。软件架构师应该着力于将大部分处理逻辑都归于不可变组件中,可变状态组件的逻辑应该越少越好

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiang木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值