如果我可以将Go的一项功能导出为其他语言,那就是界面。 -拉斯·考克斯

我极其简单的
Pascal编译器已经成为Habré上
两 本出版物的主题。 自编写以来,该语言已经获得了标准Pascal所需的所有缺少的工具,以及Borland在其黄金时代添加的许多好东西。 编译器还学习了一些最简单的局部优化,足以使您在查看反汇编程序清单时不会流血。
尽管如此,面向对象编程的丛林仍然完全未被触及。 那么,为什么不充当编译器在这一领域进行试验的测试平台呢? 为什么我们不从题词中的Russ Cox的话中汲取灵感呢? 让我们尝试在Pascal中实现Go风格的方法和接口。 这个想法很有趣,仅仅是因为过去所有流行的Pascal编译器(Delphi,Free Pascal)实质上都是从C ++借用了对象模型。 有趣的是,从Go借来的一种完全不同的方法是如何在同一领域扎根的。 如果您准备好以相当讽刺的方式跟着我,那么就丢掉“为什么?”这个问题,并把发生的事情当作一场比赛,欢迎大家来关注。
原则
通过“ Go风格”,我们将了解一些原理,并以此为基础在Pascal中实现方法和接口:
- 没有类,对象,继承的独立概念。
- 该方法可以针对任何特定的数据类型实现。 您不需要更改类型本身的声明。
- 接口与实现接口声明中列出的所有方法的任何特定数据类型兼容。 特定数据类型的广告无需指明其实现了接口。
实作
为了声明方法和接口,在新角色中使用了标准的Pascal关键字for和interface。 没有输入新的关键字。 “
for
”一词
for
指示方法的接收者的名称和类型(使用Go术语)。 这是先前声明的带有
Name
字段的
TCat
类型的示例方法描述:
procedure Greet for c: TCat (const HumanName: string); begin WriteLn('Meow, ' + HumanName + '! I am ' + c.Name); end;
接收者实际上是该方法的第一个参数。
接口是常规的Pascal条目,在声明中,单词
record
被单词
interface
代替。 在此记录中,不允许声明除过程类型的字段以外的任何其他字段。 此外,一个隐藏的“
Self
字段将添加到录制的开始。 它存储指向该特定类型数据的指针,该指针将转换为接口类型。 这是接口声明示例:
type IPet = interface Greet: procedure (const HumanName: string); end;
将特定类型转换为接口时,编译器会检查接口所需的所有方法是否存在以及它们的签名是否匹配。 然后,它设置
Self
指针,并使用指向特定类型方法的指针填充接口的所有过程字段。
与Go相比,Pascal的当前接口实现具有局限性:无法动态查询已转换为接口类型的特定数据类型。 因此,空接口是没有意义的。 下一步的发展也许就是填补这一空白。 但是,即使以目前的形式,接口也提供了多态性,可用于许多并非如此琐碎的任务。 我们将考虑一个这样的问题。
例子
使用接口的一个很好的例子是使用射线追踪方法
渲染三维场景的
程序 。 场景由简单的几何体组成:平行六面体,球体等。从观察者的眼睛发出的每条光线都必须进行跟踪(通过其所有反射),直到到达光源或到达无穷远为止。 为此,将“
Intersect
方法分配给每种类型的身体,该方法计算射线撞击身体表面的点和该点的法向分量的坐标。 对于不同类型的物体,此方法的实现方式有所不同。 因此,有关主体的信息可以方便地存储在接口条目
Body
的数组中,对于该数组的所有元素,依次调用
Intersect
方法。 接口根据主体的类型将此调用重定向到特定的方法。
这看起来像是以描述的方式构造的场景:

该程序的整个源代码(包括场景描述)共367行。
总结
事实证明,在Pascal自己的编译器中,最简单的多态实现很容易,并且很快取得了成果。 动态定义特定数据类型(转换为接口类型)的任务可能会带来一些复杂性。 努力还需要消除与标准Pascal的标准类型检查机制的明显冲突。 最终,除了对接口的所有担忧之外,Microsoft在启动一些编译示例时因Windows Defender的错误警报而继续进行不平等的斗争。