我最近开始致力于开发用 C 编写的 API。我看到一些需要 8(八个)参数的子例程,在我看来,在调用该特定子例程时传递 8 个参数看起来既丑陋又麻烦。我想知道是否可以实施更可接受和更清洁的方式。
9 回答
如果多个参数可以在逻辑上组合在一起,您可以考虑创建一个包含它们的结构,然后将该结构简单地作为参数传递。例如,您可以传递一个结构,而不是传递两个坐标值x和y 。POINT
但是,如果这样的分组不适用,那么如果你真的需要它们,那么任何数量的参数都应该没问题,尽管这可能表明你的函数做得太多了,你可以将工作分散到更多但更小的函数上.
函数调用中的大量参数通常会显示设计问题。有一些方法可以明显减少参数的数量,例如创建传递而不是单个变量或具有全局变量的结构。我建议您不要做任何这些事情,并注意设计。那里没有快速或简单的修复,但必须维护代码的人会感谢你。
是的,8 几乎肯定太多了。
这里有一些老派的软件工程术语。内聚和耦合。内聚是子例程自身结合的好坏,耦合是例程之间的接口的干净程度(或例程的自给自足程度)。
对于耦合,通常越松越好。仅通过参数进行接口(“数据耦合”)是良好的低耦合,而使用全局变量(“公共耦合”)是非常高的耦合。当您有大量参数时,通常的情况是有人试图用一层薄薄的数据耦合来隐藏他们的共同耦合。其糟糕的设计与油漆工作。
有了凝聚力,越高(越凝聚力)越好。任何修改八种不同事物的例程也很可能遭受低凝聚力的影响。我必须查看代码才能确定,但我敢打赌,用简短的一句话清楚地解释该例程的作用是非常困难的。看不见的景象,我猜它在时间上是有凝聚力的(只是一堆需要大致同时完成的事情)。
8 可能是一个合适的数字。或者可能这 8 个中的许多都应该属于一个适当的类作为成员,然后您可以传递该类的单个实例……仅通过这种高级讨论很难说。
编辑:在 c - 在这种情况下,类将类似于结构。
我也建议使用结构。也许您想重新考虑您的 API 设计。
请记住,您的 API 将被开发人员使用,并且很难使用 8 参数函数调用。
如果 API 看起来有这么多参数很麻烦,请使用指向结构的指针来传递以某种方式相关的参数。
如果没有亲眼目睹,我不会评判你的设计。有些合法函数需要大量参数,例如:
- 具有大量多项式系数的滤波器。
- 使用子像素掩码和 LUT 索引进行色彩空间变换。
- n 维不规则多边形的几何算术。
也有一些非常糟糕的设计导致大参数原型。如果您寻求更贴切的回应,请分享有关您的设计的更多信息。
某些 API(如pthreads
)使用的一种模式是“属性对象”,它被传递给函数而不是一堆离散的参数。这些属性对象是不透明的结构,具有创建、销毁、修改和查询它们的功能。总而言之,这比简单地将 10 个参数转储到一个函数需要更多的代码,但它是一种更健壮的方法。有时,一些可以使您的代码更易于理解的额外代码是值得的。
再一次,有关此模式的一个很好的示例,请参阅pthreads
API。几个月前,我写了一篇关于 API 设计的长篇文章,这是我在其中提到的一个方面。pthreads
不是从丑/不丑的角度而是从性能的角度考虑这个问题似乎也很有趣。
我知道有一些x86 调用约定可以使用寄存器来传递两个第一个参数,并使用堆栈来传递所有其他参数。所以我认为,如果使用这种类型的调用约定,并且在函数需要超过 2 个参数的情况下始终使用指向结构的指针来传递参数,那么函数调用可能会更快。在 Itanium 上,寄存器总是用于将参数传递给函数。
我认为这可能值得测试。
您可以做的另一件事是将一些参数转换为 state。OpenGL 函数的参数较少,因为您有如下调用:
glBindFramebuffer( .. ) ;
glVertexAttribPointer( .. ) ;
glBindTexture( .. ) ;
glBindBuffer( .. ) ;
glBindVertexArray( .. ) ;
// the actual draw call
glDrawArrays( .. ) ;
所有这些(glBind*
类型调用)都代表“改变状态”,所有这些都会影响下一次绘制调用。想象一个有 20 多岁的参数的抽奖电话.. 绝对无法管理!
用于绘图的旧 Windows C api 也有状态,存储在“不透明指针”对象 ( HDC
's, HWND
's..) 中。不透明指针基本上是 C 语言创建您无法直接访问的私有数据成员的方式。例如,在 Windows 绘图 API 中,您可以通过createDC
. 您可以通过函数设置 DC 的内部值SetDC*
,例如SetDCBrushColor
.
现在您已经设置了一个带有颜色和所有内容的 DC,您可以使用该Rectangle
功能将其绘制到 DC 中。您传递了一个HDC
作为第一个参数,其中包含有关使用什么颜色画笔等 的信息,Rectangle
然后只需要 5 个参数,即hdc
、x、y、宽度和高度。