在第一学期中,我们通过Java和UML介绍了OOP概念,例如封装,数据隐藏,模块化,继承等。(Java是我的第一门编程语言)
这些都不是面向对象的概念。它们都存在于OO之外,独立于OO,甚至有很多是在OO之前发明的。
所以,如果你认为这是什么OO是一回事,那么你的结论是正确的:你可以做所有这些过程中的语言,因为他们什么都没有做OO。
例如,关于模块化的开创性论文之一是关于将系统分解为模块的准则。那里没有提及OO。(它写于1972年,那时OO虽然已经有十多年的历史了,但它仍然是一个晦涩的小众市场。)
尽管数据抽象在OO中很重要,但它更多是OO(消息传递)主要功能的结果,而不是定义功能。另外,记住有不同种类的数据抽象也很重要。当今使用的两种最常见的数据抽象类型(如果我们忽略“什么也没有抽象”(可能仍比其他两种组合使用的更多))是抽象数据类型和对象。因此,仅需说“信息隐藏”,“封装”和“数据抽象”,您就对OO没说什么,因为OO只是数据抽象的一种形式,而实际上这两种形式根本不同:
- 对于抽象数据类型,抽象的机制是类型系统。隐藏实现的是类型系统。(类型系统不必一定是静态的。)使用对象时,实现隐藏在过程接口的后面,该过程不需要类型。(例如,可以使用闭包来实现,就像在ECMAScript中一样。)
- 使用抽象数据类型,不同ADT的实例彼此封装在一起,但是同一 ADT的实例可以检查和访问彼此的表示形式和私有实现。对象总是封装在所有内容中。只有对象本身可以检查其自己的表示形式并访问其自己的私有实现。没有其他对象,甚至没有相同类型的其他对象,相同类的其他实例,具有相同原型的其他对象,该对象的克隆或任何可以执行的操作。无。
顺便说一下,这意味着在Java中,类不是面向对象的。同一类的两个实例可以访问彼此的表示形式和私有实现。因此,类的实例不是对象,它们实际上是ADT实例。interface
但是,Java 确实提供了面向对象的数据抽象。因此,换句话说:在Java中,只有接口的实例才是对象,而类的实例则不是。
基本上,对于类型,只能使用接口。这意味着方法和构造函数的参数类型,方法的返回类型,实例字段,静态字段和局部字段的类型,instanceof
运算符或强制转换运算符的参数以及泛型类型构造函数的类型参数必须始终为接口。一个类只能在new
运算符之后直接使用,而不能在其他地方使用。
例如,对于模块化,我们可以将程序分为许多小程序,这些小程序执行定义明确的任务,其代码包含在单独的文件中。这些程序将通过其定义明确的输入和输出相互交互。可以对文件进行保护(加密?)以实现封装。为了重新使用代码,我们只要在新程序中需要它们时就可以调用它们。这不是捕获所有的OOP还是我缺少非常明显的东西?
您所描述的是 OO。
这确实是考虑面向对象的好方法。实际上,这几乎就是OO的原始发明者所想到的。(Alan Kay进一步走了一步:他设想了许多小型计算机通过网络相互发送消息。)您所说的“程序”通常称为“对象”,而通常所说的“发送”而不是“发送消息” ”。
面向对象是关于消息传递(也称为动态调度)的。“面向对象”一词是由Smalltalk的首席设计师Alan Kay博士创造的,他这样定义:
对我而言,OOP意味着仅消息传递,本地保留和保护以及状态过程的隐藏以及所有事物的极端后期绑定。
让我们分解一下:
- 消息传递(如果您不熟悉Smalltalk,则为“虚拟方法分派”)
- 状态过程应该是
- 万物的极端后期绑定
实现明智的,消息是后期绑定过程调用,如果过程调用后期绑定,那么你就不能在设计时知道是什么,你要调用,所以你不能对状态的具体表现做任何假设。因此,实际上是关于消息传递的,后期绑定是消息传递的一种实现,封装是它的结果。
后来他澄清了“ 大想法是'消息传递' ”,并遗憾地称其为“面向对象”而不是“面向消息”,因为术语“面向对象”将重点放在不重要的事物上(对象)并分散真正重要的内容(消息传递):
轻轻提醒一下,我在上一次OOPSLA上付出了一些努力,试图提醒大家,Smalltalk不仅是其语法或类库,甚至与类无关。很抱歉,我很久以前为该主题创造了“对象”一词,因为它使许多人专注于较小的想法。
最大的想法是“消息传递”-这就是Smalltalk / Squeak的核心所在(这在我们的Xerox PARC阶段中从未完全完成)。日语有一个小字-ma-表示“介于两者之间的那个”-也许最接近的英语等效词是“ interstitial”。制作出色且可扩展的系统的关键在于设计模块的通信方式,而不是设计其内部属性和行为。想想互联网-要生存,它(a)必须允许超出任何单一标准的许多不同种类的想法和实现,并且(b)允许这些想法之间具有不同程度的安全互操作性。
(当然,今天,大多数人甚至不专注于对象,而是专注于类,这是错误的。)
消息是根本,以面向对象,无论是作为隐喻和作为一种机制。
如果您向某人发送消息,您将不知道他们如何处理该消息。在只有你可以观察到的东西,是他们的反应。您不知道他们是否自己处理了消息(即对象是否具有方法),是否将消息转发给其他人(委托/代理),甚至他们是否理解。这就是封装的全部内容,这就是OO的全部内容。只要代理响应您的期望,您甚至都无法将其与真实事物区分开。
“消息传递”的一个更“现代”的术语是“动态方法分派”或“虚拟方法调用”,但它失去了隐喻,而专注于该机制。
因此,有两种方法可以查看Alan Kay的定义:如果单独查看它,您可能会发现消息传递基本上是一个后期绑定过程调用,而后期绑定则意味着封装,因此我们可以得出结论:#1和#2实际上是多余的,并且OO都是关于后期绑定的。
但是,他后来澄清了重要的事情是消息传递,因此我们可以从另一个角度看待它:消息传递是后期绑定。现在,如果消息传递是唯一可能的事情,那么#3将是微不足道的:如果只有一件事,并且该事物是后期绑定的,那么所有事物都是后期绑定的。再一次,封装来自消息传递。
威廉·R·库克(William R. Cook)重新审视的《关于理解数据抽象》以及他关于“对象”和“面向对象”的简化现代定义的提案中也提出了类似的观点:
动态分配操作是对象的基本特征。这意味着要调用的操作是对象本身的动态属性。操作不能被静态地识别,并且通常没有办法确切地知道响应给定请求将执行什么操作,除非通过运行它。这与始终动态分配的一流函数完全相同。
在Smalltalk-72中,甚至没有任何物体!有仅是得到了分析,重写和重新路由信息流。首先出现的是方法(解析和重新路由消息流的标准方法),后来出现的是对象(共享某些私有状态的方法组)。继承来得晚了很多,而类仅仅是作为支持继承的方式引入的。如果Kay的研究小组已经了解原型,那么他们可能根本不会引入类。
本杰明·皮尔斯(Benjamin Pierce)在类型和编程语言中提出,面向对象的定义特征是开放递归。
因此:根据Alan Kay的说法,面向对象就是消息传递。根据William Cook所说,面向对象是关于动态方法分配的(实际上是同一回事)。根据本杰明·皮尔斯(Benjamin Pierce)的观点,面向对象是关于开放递归的,这基本上意味着自我引用是动态解析的(或者至少是一种思考的方式),或者换句话说,消息传递。
正如您所看到的,创造“ OO”一词的人对对象有一种形而上的形而上学的观点,Cook则具有相当务实的观点,而Pierce则具有非常严格的数学观点。但是重要的是:哲学家,实用主义者和理论家都同意!消息传递是OO的支柱之一。期。
注意这里没有提到继承!对于OO而言,继承不是必不可少的。通常,大多数OO语言都具有某种实现重用的方法,但不一定必须是继承。例如,它也可以是某种形式的委托。实际上,《奥兰多条约》讨论了委托作为继承的替代方法,以及不同形式的委托和继承如何在面向对象语言的设计空间内导致不同的设计要点。(请注意,实际上,即使在支持继承的语言(如Java)中,人们实际上也被告知要避免使用继承,这再次表明对OO而言是不必要的。)