更好的阅读体验请点击 原文python
面向对象的程序设计
(Object-Oriented Programming,简记为OOP)这个概念你们都有所耳闻,目前(2017.12),在Tiobe世界语言排行榜上排前十的语言中,C语言和Assembly language(汇编)外的八种语言均原生支持面向对象的程序设计
。编程
<!--more-->数组
怎么判断一种编程语言是否支持OOP呢?看看这门语言是否支持类(class)、对象(object)、封装(encapsulation)、继承(inheritance)等功能和特性,支持这些就能够进行面向对象编程。拿Objective-C(OC)来讲,类就是Class
,对象就是instance
,万物的基类是NSObject
,这些东西在C语言里并不存在,是OC使用C语言的结构体(struct)抽象出来的产物。ruby
咱们从Objective-C的名字上也能看出一些端倪,直译过来是对象化的C语言
,固然不只是OC,排行榜前十中的C++一样是C语言的一个超集;C#和Java一样属于类C语言,把面向对象作的更加完全;PHP虽然是脚本语言,其解释器是使用C语言写的;而咱们常说的Python,其全称则是CPython,也是用C语言实现的解释器,固然Python解释器也有Java和C#实现的版本。闭包
为何C语言,比其余语言显得更底层呢?接触过的朋友相信都有很深的体会,C语言的程序,是在和图灵机硬件打交道,变量、数组、结构体,声明在堆内存就要为其分配内存空间大小,分配了内存,就要手动回收;数组还要区分静态和动态,每块数据占几个字节,躺在内存的什么位置,一切都按编程人员的安排。因此有人说C语言就是一个高级汇编,想起来确实有一分道理(笑)。但在智能手机、移动计算机计算能力大大提高的今天,计算资源早已不是通用编程首先考虑的问题,相比于C语言强迫编程人员从机器的角度设计程序,抽象程度更高的OOP才更接近人脑的思惟方式,才更适合提升软件工程师的编程效率。框架
即便如此,仍有一部分人至今站在OOP的对立面,从代码复杂度、建模能力要求等方面提出异议,坚持写C++、Python、PHP的时候不构造类,写纯过程的程序。但其实,这些自称为原C党的朋友,并不能说本身没有使用OOP,由于这些语言中变量,跟C语言中的变量,有本质的不一样。编程语言
就用字符串
和数组
来举例子,C语言是没有string类型的,只有字符数组,用\0
来标记字符串结束;而其余语言中的string则是早已封装好的字符串类(Class),用起来跟整型无异。函数
C语言中字符串和数字变量声明设计
char name[] = "Tom\0"; int age = 12;
Python中字符串和数字变量声明code
name = "Tom" age = 12
C++中字符串和数字变量声明
string name = "Tom"; int age = 12;
咱们在Python和C++中使用字符串,早已不是在直接与设备内存打交道,而C语言中的“字符串”还停留在只是内存中的一段连续空间的阶段。
再来看一看数组,C++虽然也支持C的数组,但我想对比的实际上是C++标准库中的向量(Vector),以及Python中的链表(List),这些高级容器一样是基于OOP理念设计的类,仍只有C语言的数组内容直接映射在内存上。
因此即便你不构造Class,在C++、Python、PHP中仍在使用对象和实例的OOP特性,即便开发的是线性程序。
常常会看到有人抱怨Java把面向对象的理念作的太过头,C#做为Java的仿制品,也一样逃脱不了被诟病的现实,但其稳定性也是有口皆碑。然而真正把OOP理念实现的彻头彻尾不折不扣的,反而是最先的OOP语言之一的Smarttalk,让我先看一段Samrttalk的代码
Transcript show: 'Hello world'
这是Smarttalk版本的Hello world
程序,Transcript
是Squeak(这是Smalltalk语言的一种版本实现)环境里,把信息显示到屏幕上的一个对象。这段代码是用冒号给这个对象发送了一个消息(Message),若是给这段代码加上一对中括号,是否是像极了Ojective-C,没错,由于OC就是参考Smarttalk设计的Runtime。
一样,Samrttalk也支持中括号的写法,咱们能够把上面的一段代码段落,赋值给一个变量:
t := [ Transcript show: 'Hello world']
这个t变量,实际上是一个闭包(BlockClosure)对象,相同的概念在C++ 11标准里才出现,相比之下Smarttalk的设计理念真的很前卫。而OC做为Smarttalk的追随者,更是拥有NSOperation类来实现闭包,相比之下,block并非基于OOP的设计。
C++的blcok和ObjC的NSOperation,这里block写法OC一样支持
void hello = ^ { NSLog(@"hello world"); }; hello(); NSBlockOperation* block = [NSBlockOperation blockOperationWithBlock:^{ // 作一些操做 }]; [[NSOperationQueue mainQueue] addOperation:block];
要注意的是NSBlockOperation
是在OC支持block之后才出现的类,在此以前要使用NSOpertaion,咱们须要继承NSOpertaion类,并重写这个类的-(void)main
方法,这无疑是一件十分繁琐的事。
OC做为Smarttalk的追随者,在OOP的理念上是要强于C++、Python和PHP的,interface
、implementation
、getter
、setter
的接口设计,和Java、C#相互参考,水平相近,但仍比Smarttalk和Ruby略逊一筹。
熟悉Cocoa框架的朋友都知道,UI绘制框架CoreGraphic
中仍然要使用大量的CG开头的C语言函数,点、线、面的容器,依旧是CGPoint,CGSize,CGRect这些C语言结构体;数字变量依然是int、NSInteger、NSNumber(数字类)混着用,相互转换忙的不亦乐乎。固然这一切在OC支持字面量特性(Literals)之后有了好转:
//经过@符号直接把普通变量转换为数字对象 NSNumber *myIntegerNumber = @8; //转回来 NSInteger customNumber = [myIntegerNumber integerValue];
相比之下,Smarttalk和Ruby作的更完全,更好用,下面是用Smarttalk重复输出十次Hello world
的代码,给数字10发timesRepeat
消息,重复消息参数中的闭包:
10 timesRepeat: [Transcript show: 'Hello world']
为何整数类要设计这么方法呢?由于Smarttalk中并无循环语法,甚至其余语言常见的条件语句if/else在Smarttalk中都是不存在的,而都是使用OOP的理念实现,有兴趣了解更多关于Smarttalk的内容,请来这里。
这里咱们从继承Smarttalk理念的Ruby提及,虽然其使用点语法替代了冒号,但仍能看出Ruby中的数字类型,就是数字对象。
//将数字对象102转换成字符串对象 102.to_s
用Smarttalk实现则是
102 printString
相比之下Python则像是一个做者对OOP还处于感性认知阶段设计出来的语言,因此会设计出len()、map()、fliter()这种C语言函数风格的接口,例如我在OC中咱们获取数组的长度使用count属性,使用点语法或者中括号消息均可以获取(关于OC中的点语法和中括号语法咱们后面再聊)
NSArray* a = @[@(1),@(2),@(3)]; a.count; [a count];
这很面向对象,由于咱们要获取数量的主体数组实例a,发消息让他返回长度很符合人类的思惟逻辑。一样的咱们看看Ruby,也是同样的操做
a = [1,2,3] a.length a.size
然而当我使用第一次写Python代码的时候,我经历了不少人都遇到过的状况,不知道字符串或者数组如何获取长度。由于Python中string和list都没有length、size、count、len等属性和方法,而后咱们发现Python提供了一个len()方法获取序列长度,这个方法接受一切的对象做为参数。
a = [1,2,3] len(a) s = "123" len(s)
针对这个问题,有一部分人认为不是问题,他们说作OOP不要太教条主义,len在前在后能有很大差异么?我想说真的是有的,这个看似简单的先后问题,其实影响了实际的编程体验,就是是否基于对象思考问题的体验。
一方面,len()方法像一个凭空存在的方法,不依赖于任何类和对象,也不是依附于某个模块,知道它存在,才会去使用它,一样的还有Python中的type()、map()方法等。另外一方面,这一类方法到底能够用于什么类型的对象,开发者内心也没底,必须对照接口标明的参数类型使用。
这一切无疑不利于程序开发的思惟连贯性,有朋友可能以为我说的言过其实,我这里举一个例子你们体会一下何为思惟连贯性。
需求是将一段英文字符串的单词逆序,How are you
处理成you are How
。
咱们用OC实现以下:
#import <Foundation/Foundation.h> NSString* reverse(NSString* text) { NSArray *words = [text componentsSeparatedByString:@" "]; NSArray *reversed = [[words reverseObjectEnumerator] allObjects]; return [reversed componentsJoinedByString:@" "]; }
Python实现为
def reverse(text): a = text.split(' ') a.reverse() return ' '.join(a)
Ruby的实现为
def reverse(string) return string.split.reverse.join(' ') end
观察出来区别了了吧,重要的不是Ruby只用了一行代码,而是Ruby相比于OC和Python,省去了不少中间变量,别看只是一点点节省,其实省去咱们实际开发中很大一部分无用工做。固然,OC能够经过括号多层嵌套连贯起来写,也能达到一样的效果,但咱们并不推荐这样作,由于OC的方法名偏长,若是缩进不当,会让代码更难理解。
相比之下,Python的接口设计更滑稽一些,首先在Ruby中
array.reverse! array.reverse
是两个不一样的方法,前者只逆转array,没有返回值。后者则返回一个新的逆转数组对象,Python没有相似设计。
其次Ruby和OC都将join方法设计在array类里,惟独Python将其设为字符串类型的方法,致使了Python无法连贯地将中间参数略去。