Cocoa改造前篇 - 說在前面的

更好的閱讀體驗請點擊 原文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特性,即便開發的是線性程序。

完全的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的,interfaceimplementationgettersetter的接口設計,和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無法連貫地將中間參數略去。

相關文章
相關標籤/搜索