多态性并不是 OOP 或类似 OOP 的系统所独有的,但正如您所指出的,它们被认为是为软件中被认为是普遍想法的东西提供标准模式。它可以用没有一流支持的语言来模拟(并且在某些语言中具有语法帮助,例如 lua)。
在旧的过程语言中,人们使用了与 C++ 相同的技巧,只是或多或少是明确的;通常是一些动态分配的数据的 void * 以及一些用于对其进行操作的函数指针。标准库qsort 函数在一定程度上体现了这一点。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Sortable {
char *(*toString)(struct Sortable *p);
} Sortable;
typedef struct AThing {
Sortable p;
int value;
} AThing;
typedef struct BThing {
Sortable p;
const char *str;
} BThing;
int sortableCompare(const void *a, const void *b) {
int res;
Sortable *aa = *((Sortable **)a);
Sortable *bb = *((Sortable **)b);
char *astr = aa->toString(aa);
char *bstr = bb->toString(bb);
res = strcmp(astr, bstr);
free(astr);
free(bstr);
return res;
}
char *AThing_toString(Sortable *p) {
AThing *self = (AThing*)p;
char *res = malloc(15);
snprintf(res, 15, "%d", self->value);
return res;
}
char *BThing_toString(Sortable *p) {
BThing *self = (BThing*)p;
return strdup(self->str);
}
AThing *createIntSortable(int i) {
AThing *self = malloc(sizeof(*self));
self->p.toString = AThing_toString;
self->value = i;
return self;
}
BThing *createStringSortable(const char *str) {
BThing *self = malloc(sizeof(*self));
self->p.toString = BThing_toString;
self->str = str;
return self;
}
int main() {
Sortable *array[3];
array[0] = &createIntSortable(1)->p;
array[1] = &createStringSortable("hi")->p;
array[2] = &createIntSortable(3)->p;
qsort
( array // auto decay and coerce to void*
, sizeof(array) / sizeof(array[0]) // nelem
, sizeof(array[0]) // elem size
, &sortableCompare // compare function
);
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {
char *p = array[i]->toString(array[i]);
printf("%d %s\n", i, p);
free(p);
}
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {
// To be more fancy, make a destructor in p
free(array[i]);
}
}
go 和 rust 并不是严格面向对象的,而是使用接口和鸭子类型转换来赋予所有类型很多与对象相同的属性。您可以使用方法来装饰任何东西,并选择 self 参数是通过引用还是通过值。
Haskell 有类型类,这有点类似于 go 和 rust 的多态风格,因为提供类型的人提供了该类型的接口的实现(例如,可以充当列表的对象可能实现Monoid)。然后,任何采用实现 monoid 的对象的函数都可以接收将对象自动强制转换为 Monoid 类型类实现。
在没有类型类的函数式语言中(甚至大部分时间在 haskell 中),人们通过编写通用的高阶函数然后传入他们关心的对象的操作集来实现多态性。通过这种方式,您可以拥有容器、过滤器、转换器等,它们不知道它们在做什么,而是根据自己的目标进行用户提供的一组操作。 elm-astar 就是一个很好的完整示例,因为它用一种自己没有任何多态功能的语言实现 A*。与 C 相比,它获得了一些简洁性,因为虽然多态不是内置的,但泛型类型却是。