design-patterns - 过程语言有设计模式吗?

标签 design-patterns procedural-programming

在我的经验中,我经常在 Java 等面向对象语言中看到一些设计模式,例如访问者模式、策略模式……但在 C 等过程语言中我没有看到太多模式……我想知道那些模式存在于过程语言中吗?

最佳答案

过程语言确实有设计模式。但是由于程序方法通常被忽略,而倾向于基于类的 OOP,因此它们并未得到广泛认可。
我用 C 开发高性能软件,并且有几种重复出现的模式。因此,我将提供一些我经常看到的模式的见解。
Handlebars
这就是在过程编程中进行封装的方式。构造函数不返回结构或对象。但是一个句柄:它通常是一个不透明的指针或只是一个整数。你不能用它做任何有趣的事情,因为它只是一个数字。细节完全隐藏。但是您可以将此句柄传递给处理它的函数:
例子:

  • 在 Windows 上 CreateWindow函数返回一个 HWND。这是一个窗口的句柄,可以传递给其他函数,如 ShowWindow、DestroyWindow 等。
  • 在 Linux 上 open系统调用。返回 just 和 int。这是一个文件句柄。

  • 上下文
    对象在过程语言中通常称为上下文。 Context 是一个结构体,它包含某个系统的状态,就像对象的成员一样。在 OOP 中你写 object.method(parameter) .在过程式编程中你写 function(addressOfContext, parameter) .内部函数直接使用上下文结构,而公共(public)函数只接受一个句柄,实现将其解析为实际的上下文结构。
    回调
    或者函数指针。函数的用户传递其函数的地址以向系统添加自定义行为。这就是多态在过程编程中的实现方式。这允许编写通用函数。
    一个显着的例子是 qsort C 函数。这需要一个元素数组的地址。获取一个元素有多大、数组中有多少元素以及执行比较的比较器函数。这是完全通用的实现,允许对各种数据进行排序。
    设置结构
    当一个函数可以通过多种方式参数化时。通常使用设置结构。
    规范往往要求这些结构体默认为零填充,只填充相关成员。如果某些成员是互斥的,则将它们放在一个联合中。这种设置结构的典型例子是 WNDCLASS来自 WinAPI。
    可变尺寸数据
    嗯,这与其说是通用设计模式,不如说是一种 C 模式。有时对象可能持有任意大小的二进制有效载荷。这种模式通常发生在从二进制文件读取数据时,因为它可以包含多种类型的数据块。这是由这样的结构完成的。
    typedef struct
    {
        int someData;
        int otherData;
        int nPayloadLength;
        unsigned char payload[1];
    } VariableSized;
    
    在代码中完成了以下操作:
    VariableSized *vs = malloc(sizeof(VariableSized) + extraLength);
    
    这分配的内存大于允许可变长度有效负载空间的结构。然后可以通过例如访问其第 5 个字节。 vs->payload[4] .
    这样做的好处是可以一次释放整个对象free称呼。并且保证它在内存中有一个连续的块。因此,它比在堆中的其他地方分配相应的缓冲区更好地利用缓存。
    OOP 设计模式的过程对应物
    在过程语言中,OOP 模式从不以它们的名字命名。所以我只能在这里猜测。
    创作模式
  • 抽象工厂 :抽象工厂通常是单例。在这种情况下,根本不使用此模式,而是使用条件编译。否则设置结构提供创建功能。
  • build 师 : 使用设置结构。
  • 工厂方法 :回调用于创建。
  • 延迟初始化 : 在 C++ 中,静态局部变量用于此目的。在 C 中,您可以使用 if (!initialized) { initialize(); initialized = 1; }在对性能不重要的地方使用模式。对于性能关键代码,根本不使用延迟加载。用户必须找到一个地方来初始化上下文。
  • 样机 :在程序世界中,我们只是返回库存对象的句柄。一个例子是 GetStockObject WinAPI 中的函数。对于可变对象,出于性能原因,通常使用写时复制机制。
  • 单例 : 只需编写顶级函数(并在您绝对需要全局状态时使用全局变量)。

  • 结构模式
  • 适配器 门面 :一种在现有界面上构建另一个界面的模式。简单的新函数将调用旧函数和其他函数。
  • : 具体实现的回调以设置结构的形式提供。
  • 复合 : 顶级函数用于指定它应该操作的父节点的句柄。
  • 装修 :装饰行为以回调的形式提供。或者为所有可能的装饰提供一个事件处理程序回调,这些装饰接收各种消息并决定是否处理它们(例如 WinAPI 中的窗口过程)。
  • 蝇量级 : 用于打包在结构和数组中的只读二进制数据。
  • 代理 : 与 OOP 中的几乎相同,但没有类。

  • 行为模式
  • 责任链 :循环遍历的回调数组或链表。规范描述了回调应该如何表明它们处理了导致循环中断的请求。
  • 命令 : 命令是包含 do 的结构体和 undo打回来。这些回调通常需要某种上下文来操作。并维护一系列命令以执行撤消。
  • 翻译 :使用 lex 和 yacc 编写或生成编译器/解析器/解释器。
  • 迭代器 : 使用句柄,其他相同。出于 C 中的性能原因,我们经常坚持使用数组。
  • 调解员 : 通常通过使用一些消息分发机制和message loops来实现和事件处理程序。
  • 纪念品 : 与 OOP 相同,但没有类。
  • 观察员 :与责任链相同,但循环不会中断。一个例子是 atexit .
  • 国家 :通过将当前状态和请求的操作映射到一个函数中的二维调度表来实现。 (在稀疏情况下,只使用 ifs。)
  • 策略 :这是回调的基本用例。
  • 模板方法 :通常框架允许用户为某些函数提供他自己的回调。库通常提供一种使用自定义内存分配函数的方法,提供自定义 mallocfree .
  • 访客 :通过使用回调的多维数组实现,通常在开始时填充 NULL(对于默认行为),并填充到每个类型对的主要初始化代码中。
  • 关于design-patterns - 过程语言有设计模式吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10491175/

    相关文章:

    Eclipse控制台: detect warning and error patterns and make them clickable

    java - 与其过程计数器部分相比,优化 Java 8 功能算法

    python - 如何正确测试程序 python 脚本

    sql - 错误报告 : ORA-06550: line 7, 列 134 : PL/SQL: ORA-00913: too many values ORA-06550: line 7,

    api - Eclipse 中的 IProject.setDescription 使用什么设计模式

    c++ - 如何避免这种类似单例的设计模式?

    design-patterns - 如何灵活地存储产品的多个价格?

    c# - C# 中的通用标识映射。不想要公共(public)构造函数

    c++ - 将 C++ 类转换为过程 API : common way of replacing member variables?

    documentation - 如何记录过程编程?