面向对象编程(Object Oriented Programming,OOP)是软件开发方法,更愿意说它是一种编程思想。
面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术 发展到一定阶段后的产物(从面向过程发展到面向对象)。
简单理解面向对象编程思想。
为什么是简单理解呢?因为面向对象这个思想具体在语言中的实现,得学完整个语言之后,在实际编程过程中再一步步理解加深,最后顿悟滴。
面向对象 简单点理解就是面向的这个世界是由对象构成的,面向的程序世界也是由对象构成的。
将抽象的问题又还原到现实世界,用现实世界的思维方法来解决问题。
面向对象方法是以认识论为基础,用对象来理解和分析问题空间,并设计和开发出由对象构成的软件系统(解空间)的方法。 由于问题空间和解空间都是由对象组成的,这样可以消除由于问题空间和求解空间结构上的不一致带来的问题。简言之,面向对象就是面向事情本身,面向对象的分析过程就是认识客观世界的过程。
面向对象方法本质上还是以对象为主体,如同人类认识这个世界一样,是从看到了各种各样的实体对象出发,然后引申出了各种概念类别。面向对象方法也是从对象出发,发展出对象,类,消息,继承等概念。
世界里一个个对象有自己的属性和动作(方法),比如:三胖(对象)有腰围(属性),三胖能笑(方法)。然后一个个对象相关联起来,解决问题。
对象就好比存在于三胖国家具体的人,他们之间通过互相传递信息(调用方法)来完成整个国家的运转。
上图很有意义,后面可以根据OC语言来具体理解。表急。
然,对象太多、太杂乱了、又不好整理,于是就有了抽象概念—类。
类就是,就是如下:
猫就是一个类,因为你不知道他是指具体那一只猫,而猫这个类是抽象于具体的猫,将Kitty、Garfield、Doraemon这些所有(对象的)共性抽象出来,形成一个“猫类”–“类”。
当你要制造一个新的猫来,你就可以,就要根据这个“猫类”的模板进行创建,然后再加上一些个性。比如涂成黑色,性别为女,能变身成性感女人,绑束紫色马尾长发,金黄色双眼,褐色肌肤,当然少不了大胸器,创建出来的就是“四枫院夜一”,创建出来的绝逼是一只猫。
不要回答先有鸡蛋。一点都不好笑。
问题一个问题:先有对象还是先有类?
回答这个问题也很简单,先有人还是先有人类这个概念?
众所周知的类(Class),就是从对象上抽象出来的概念。
类其实是抽象认知能力作用于程序世界的基本元素——对象后,所衍生出来的抽象概念,是抽象思维在程序世界中物化后的产物。当然,现实世界中每个对象都有无数的数据和逻辑,但在具体到程序世界时,我们往往只关心具体场景中相关的数据(属性)和逻辑(方法)。
知道了类是怎么来的,那么类的作用是什么,我们为什么需要类呢?
类可以帮助我们方便地认识和定义世界中的对象。这个作用是显而易见的。如:看到一个新的人,怎么知道这是个人呢?引用人类这个概念来认识就可以判定了。(例子有点夸张)
上文说到,对象是基本,我们从对象上抽象出类。但是,世界可并不是一层对象一层类那么简单,对象抽象出类,在类的基础上可以再进行抽象,抽象出更高层次的类。所以经过抽象的对象论世界,形成了一个树状结构。
千万不要小看了这棵抽象层次树,如果能参透其中的奥秘,就能明白很多面向对象中的玄机,而且很多问题就都迎刃而解了。这种抽象层次树理论也是后续诸多内容的理论基础。例如,OO中重要的概念——继承(Inheritance)和多态(Polymiorphism),如若探究其哲学本源,就是从这里来的。
先说明这么多了,随着后续内容的深入,还会有更多丰富的内容进来。例如,后面会看到,所谓的“里氏代换原则(LSP)”,在哲学本质上不过是在这棵树上所加的一条限制规则,而“面向接口编程”、“低耦合、高内聚”、“依赖倒置”等一系列耳熟能详的短语,归结到哲学上也只是这棵树的一些精化。
再提示一遍,这棵树非常重要,得其精髓,就能理解诸多OO中概念、原则和方法的本质。后续讨论中,抽象层次树理论将作为重要的理论基础。
从哲学和认识论角度来说,是先有对象,然后有类;先有子类,然后有父类,是一种自底向上形成的体系。而继承一词,明显带有自顶向下的暗示,因为往往是先有爷爷、有父亲继承爷爷、然后才能有儿子继承父亲。这样,就容易让人误解成是先有父类才有子类。所以,为了更好的体现继承的哲学本质,我更倾向于使用“泛化”代替“继承”。当然,由于继承一词已经被普遍使用和接受,接下来我还是会沿用继承一词,只不过希望各位时刻牢记,其实是先有了子类,才从子类泛化出父类。
当然,当父类被抽象出来后,可能还会有新的子类加进来。但是,当初父类一定是从某些子类中泛化出来的,而不会是凭空突然出现的。
OC就是单根继承,意味着所有类的继承,都继承自单一的基类(NSObject)的继承模式。
有一条原则你慢慢体会一下,那就是著名的开放-关闭原则(OCP)。
开放-关闭原则(OCP):软件实体应该可以扩展,但不可以修改。
咱改一下:类应该可以扩展,但不可以修改。
不过,要想世界正常运作,只有OCP似乎还有点问题。到目前为止,我们都是在抽象层次树已经存在,并且假定它完全正确的前提下讨论的,可是,我们并没有任何规则限制抽象层次树的正确性,例如,如果我把食堂挂到医院下,让食堂成为医院的子类,在理论上时没有错的,但如果这样随便乱规定继承关系,那么一切依赖继承正确性的原则、概念都没有意义了。所以,只有OCP是不够的,需要对继承进行一个限制。
Barbara Liskov在1987年的OOPSLA大会上发表了一篇文章——《Data Abstraction and Hierarchy》,其中提出了一个非常重要的原则,叫里氏代换原则(LSP)。
里氏代换原则(LSP):子类型应该能代替掉其父类型,且代替后程序运行情况不会错乱。
抄人家的话抄远了,回到OC上。
编程界有句老话,“只要再多添加一层间接,计算机科学中就没有解决不了的问题。”
间接:可以理解为让其他人代替自己去做某件事。
在代码中去理解就是:通过指针间接获取某个值,而不是直接获取。
变量中的间接:使用变量来代替具体的值。
文件名的间接:输入数据存在文件中。
面向对象中的间接:
间接是OOP的核心,其他一切都是通过间接产生的引申效果。
面向过程编程:是面向函数的。数据为函数服务,程序运行就是将数据(参数)通过一个个函数,最后得出结果(返回值)。
面向对象编程:是面向数据的。函数为数据服务,数据(以属性的形式)存储在对象中,借助间接的强大功能,这些数据知道如何查找相应的函数来进行操作。
这里的间接是指通过函数指针来查找相应的代码块。只要对象发送消息,就自动查找与这个消息相关的代码块执行。
对象通过指针查找代码执行。
类是一种能够实例化成对象的结构体。
下图:红圆对象–》圆类–》圆类的代码块(箭头为指针)
问题来了,为什么不让对象直接指向代码块呢,为什么要用这种间接的技术,直接指向不是更简单吗?
拥有类作间接会具有更大的优势:如果在运行时改变某个类,则该类的所有对象会自动继承这些变化。
上图为:Red Circle对象查找draw函数代码的过程。
1)查询红圆对象(draw消息目标)是属于哪一个类。
2)在Circle类中浏览其代码,查找draw函数的位置(地址)。
3)找到draw函数后,执行绘制圆形的函数代码。
上图为:Green Rectangle对象查找draw函数代码的过程。
1)查询绿矩形对象(draw消息目标)是属于哪一个类。
2)在Rectangle类中浏览其代码,查找draw函数的位置(地址)。
3)找到draw函数后,执行绘制矩形的函数代码。
@interface定义了类的公共接口。
创建某个特定类的对象之前,Objective-C编译器需要知道一些有关该类的信息,尤其是对象的数据成员(即对象的C语言类型结构体应该是什么样子)及其提供的功能。可以使用@interface指令把这些信息传递给编译器。
说简单点,就是让编程器知道这是Objective-C的接口部分代码,请记录下来,以后要用这些接口。
最后一行@end就是告诉编译器,我们已经完成了类的声明。
@implementation定义了类的实现代码。
和@interface一样是一个编译器指令,行号后没有分号哦,因为Objective-C编译器指令不必使用分号。
实例化对象分两步:1.分配内存,2.将这些内存初始化为有用的默认值。
完成这两步表示对象已经创建好了。
例:Car *car = [[Car alloc] init];
面向对象的术语:
类(class)是一种表示对象类型的结构体。是生产对象的模板。对象通过它的类来获取自身的各种信息,尤其是执行每个操作需要运行的代码。
对象(object)是一种包含值(属性)和指向其类的隐藏指针的结构体。
实例(instance)是“对象”的另一种称呼。
消息(message)是对象可以执行的操作(方法),用于通知对象去做什么。对象接收消息后,将查询相应的类,以便找到正确的代码来运行。
方法(method)是为响应消息而运行的代码(块)。
方法调试(method dispatcher)是Objective-C使用的一种机制,用于推测执行什么方法以响应某个特定的消息。
接口(interface)是类为对象提供的特性描述。
实现(implementation)是使接口能正常工作的代码