什么是类?
- 类是一组类似对象的规范。
- 对象是一组对封装数据元素进行操作的函数。
- 或者更确切地说,对象是一组对隐含数据元素进行操作的函数。
- 对象的功能意味着存在一些数据元素; 但是该数据不能直接在对象外部访问或可见。
- 它可能是; 但是没有规则说必须这样。从用户的角度来看,对象只不过是一组功能函数。这些函数操作的目标数据必须存在,但用户不知道该数据的位置。
好。什么是数据结构?
- 数据结构是一组有凝聚力的数据元素。
- 或者,换句话说,数据结构是由隐含函数操作的一组数据元素。
- 对。那么你对这两个定义有什么看法?
- 确实。它们是彼此的补充。它们像手套一样装在一起。
- 对象是一组对隐含数据元素进行操作的函数。
- 数据结构是由隐含函数操作的一组数据元素
哇,所以对象不是数据结构。
- 正确。对象与数据结构相反。
- 正确!DTO是数据结构。
- 再次纠正。数据库包含数据结构,而不是对象。
- 当然不是。数据库表和对象之间没有映射。数据库表是数据结构,而不是对象。
- 它们在数据结构之间传输数据。
- 没什么。没有对象关系映射器这样的东西; 因为数据库表和对象之间本身就无法映射。
- 不,ORM提取业务对象操作的数据。该数据包含在ORM加载的数据结构中。
- 它可能。它可能不会。这不是ORM的业务。
- 一点也不。这种区别具有重要意义。
- 例如数据库模式的设计与业务对象的设计。业务对象是定义业务行为的结构。 数据库模式是定义业务数据的结构。这两种结构受到非常不同的力量的约束。业务数据的结构不一定是业务行为的最佳结构。
- 这样想吧。数据库架构不针对一个应用程序进行调整; 它必须服务于整个企业。因此,该数据的结构是许多不同应用程序之间的折衷。
- 好。但现在看看每个单独的应用。每个应用程序的对象模型描述了这些应用的行为结构的方式。每个应用程序都有一个不同的对象模型,微调该应用程序的一些行为。
- 对!对象和数据结构受到非常不同的力量的约束。他们很少很好地协调在一起工作。人们习惯称之为对象/关系阻抗不匹配。
- 现在你的想法现在不同了。没有阻抗不匹配,因为对象和数据结构是互补的,而不是同构的。
- 它们是对立的,而不是类似的实体。
- 是的,以一种非常有趣的方式。你看,对象和数据结构意味着截然相反的控制结构。
- 想想一组所有符合公共接口的对象类。例如,想象一下表示二维形状的类,它们都具有计算形状area和perimeter形状的功能。
- 我们只考虑两种不同的类型:Squares和Circles。应该清楚的是,这两个类的area和permimeter函数在不同的隐含数据结构上运行。还应该清楚,调用这些操作的方式是通过动态多态。
- 有两种不同的area函数; 一个用于Square,另一个用于Circle。当调用者调用area特定对象上的函数时,该对象知道要调用的函数。我们称之为动态多态。
- 现在让我们将这些对象转换为数据结构。我们将使用Discriminated Unions。
- 可区分联合discriminated unions。在我们的例子中,这只是两种不同的数据结构。一个为Square,另一个为Circle。该Circle数据结构具有一个中心点,和用于数据元素的半径。它还有一个类型代码,可以将其标识为Circle。
- 当然。该Square数据结构具有左上点,并且侧的长度。它还有类型鉴别器 - 枚举。
- 对。现在考虑这个area功能。它会有一个switch语句,不是吗?
- 再一次。现在考虑这两种情景的结构。在对象场景中,area函数的两个实现彼此独立并且属于(在某种意义上的单词)各自类型。 Square的area函数属于Square,Circle的area函数属于Circle。
- 它变得更好了。如果要将Triangle类型添加到对象方案中,必须更改哪些代码?
- 对。因此,当您添加新类型时,几乎没有变化。现在假设您要添加一个新函数 - 比如center函数。
- 好!因此类添加新函数很难,您必须更改每个类。
- 对!数据结构添加新类型很难,您必须更改每个函数。
- 对!数据结构添加新函数很容易。
- 绝对是相反:
- 向一组类添加新函数很难,您必须更改每个类;将新函数添加到一组数据结构很容易,只需添加函数,不做任何其他更改。
- 将新类型添加到一组类很简单,只需添加新类即可;向一组数据结构添加新类型很难,您必须更改每个函数。
是啊。对立统一。以有趣的方式对立。我的意思是,如果您知道要将新函数添加到一组类型中,那么您需要使用数据结构。但是如果你知道你将要添加新类型,那么你想要使用类。
- 好!但今天我们还有最后一件事要考虑。还有另一种方式,数据结构和类是对立的。它与依赖关系有关。
- 是的,源代码依赖的方向。
- 考虑数据结构案例。每个函数都有一个switch语句,它根据区分联合中的类型代码选择适当的实现。
- 考虑调用area函数。调用者依赖于area函数,area函数取决于每个特定的实现。
- 想象一下,如果每个类实现area函数,都有写入了它自己的函数。因此,有circleArea和squareArea和triangleArea单独自己的实现area函数。
- 想象一下,这些函数放在不同的源文件中。
- 对。这是源代码依赖。一个源文件依赖于另一个源文件。这种依赖的方向是什么?
- 那个area函数的调用者怎么样?
- 正确。所有源文件依赖关系都指向调用方向,从调用者到实现。因此,如果您对其中一个实现进行微小更改......
- 对。至少对于依赖于源文件的日期来确定应该编译哪些模块的语言系统来说是这样的。
- 是的,有些则没有。
- 还有很多重新部署。
- 是的,因为area函数的调用者依赖于接口,并且实现函数也依赖于该接口。
- 对。实现的源文件指向调用的相反方向。他们从实现指向调用者。对于静态类型语言至少也是如此。对于动态类型语言,area函数的调用者完全不依赖于任何东西。链接在运行时得到解决。
- 只需重新编译或重新部署已更改的文件。
- 对。我们称之为依赖倒置。
总结
好的,那么让我看看我是否可以把它合起来。类和数据结构至少以三种不同的方式存在。
- 类在保持数据隐含的同时使函数可见。数据结构使数据可见,同时保持隐含的函数。
- 类使添加类型变得容易,但很难添加函数。数据结构使添加函数变得容易,但很难添加类型。
- 数据结构将调用者公开给重新编译和重新部署。类将调用者与重新编译和重新部署隔离开来。
与本文相反观点:反OOP的面向数据的编程DOP原理