【问题标题】:What the ugliest API for a relatively well known library that you have seen, and why and how could it be improved? [closed]你见过的相对知名的库中最丑陋的 API 是什么,为什么以及如何改进它? [关闭]
【发布时间】:2009-10-13 01:42:13
【问题描述】:

我一直在研究 Lucene 2.9 之间的差异,特别是重做令牌流 API,我觉得它与旧的相比特别难看,如果您重用所述令牌,则返回一个新的或用值重新填充给定的值。

我还没有进行任何分析,但似乎使用 MAP 来存储属性不是那么有效,并且创建一个新的值类型来保存值等会更容易。TokenStream 和 Attribute 的东西看起来很像对象池如今,对于简单的值类型(如文本标记)来说,几乎不需要。

【问题讨论】:

  • Win32:有史以来最丑的 API(我稍后会详细说明)

标签: coding-style code-readability


【解决方案1】:

creat()

当 Ken Thompson 和 Dennis Ritchie 获得 1983 年图灵奖时,在他们各自的获奖感言之后,听众中有人问 Ken,如果他要从头再来,他会对 Unix 做些什么不同的事情。他说,“我会用'e'来拼写'creat'。”

【讨论】:

  • 我想说“LOL”,但没有 15 个字符。
  • 我不确定丑陋,但我会为有趣的报价投赞成票。
  • 我选择这个是因为它的票数最多。还有很多其他有价值的答案,但很难选择“最好的”......
【解决方案2】:

Livelink (OpenText) API

  • 一切都以某种奇异的锯齿状数组形式返回
  • 文档完全没有提供示例
  • [您最喜欢的搜索引擎] 通常不返回给定 API 方法的结果
  • 支持论坛几乎被废弃了
  • 了解结果数据的唯一可靠方法是在 Livelink 调试器中运行数据
  • 最后...系统花费数万(数十万)美元

我办公桌旁边的墙上有我的头印...

从 API 方法中获取值的一个非常简单的示例:

var workflow = new LAPI_Workflow(CurrentSession);

// every Livelink method uses an out variable
LLValue outValue;
// every method returns an integer that says if the call was
// a success or not, where 0 = success and any other integer
// is a failure... oh yeah, there is no reference to what any
// of the failure values mean, you have to create your own
// error dictionary.
int result = workflow.ListWorkTasks(workId, subWorkId, taskId, outValue);


if (result = 0)
{
  // and now let's traverse through at least 3 different arrays!
  string taskName = outValue.toValue(0).toValue("TASKS").toValue(0).toString("TaskName");
}

啊啊啊!!! :D

【讨论】:

  • +1 :-) 这就是为什么已经创建了一些 LAPI 库包装库甚至替代 LAPI 库的原因......
【解决方案3】:

我从来都不是 java.sql 包的粉丝...

  1. 您必须为所有内容捕获已检查的异常,并且只有一个异常,因此如果不检查 SQL 代码字符串,它并不能真正说明发生了什么问题。
  2. 除此之外,您必须使用 java.sql.Date 而不是 java.util.Data,因此您始终必须为其中一个指定完整的包。更不用说两者之间必须发生的转换。
  3. 然后是参数索引,它是 1-base-indexed,而不是 Java 的其余部分,它是 0-base-indexed。

总而言之,一个非常烦人的图书馆。值得庆幸的是,Spring 库确实使它更容易使用。

【讨论】:

  • 我讨厌单个 SQLException。重复键、SQL 语法,它们以同样的方式返回到我的代码中。
【解决方案4】:

COM。它最大的改进最终是 .NET。

【讨论】:

  • 我自己没用过COM,买我已经看过了。 IIDIIIInterface 噩梦!
  • 在 Visual Studio 6 刚推出的时候,COM 非常棒。 COM 的主要丑陋之处在于它的各种包装器,例如 ATL,它使用了过多的匈牙利符号。 .NET 完全兼容 COM。
  • 我认为 COM 太糟糕了,因为 1) 微软希望它那么复杂,所以它把你束缚在使用他们的编译器上。微软以外的任何人在没有 MS 编译器的情况下实现 COM 需要很长时间,或者 2) 微软的蛋头们获得了太多的博士学位,并且不关心制作人们可以在没有微软编译器的情况下实际使用的东西
【解决方案5】:

某些java.io.File 方法对系统编程至关重要,它返回一个布尔值以指示成功或失败。如果这样的方法(例如,mkdirdelete)失败了,你根本没有办法找出原因。

这总是让我的下巴张开。

【讨论】:

  • 是的,一个非常丑陋的非 oo 封装在一些 c 代码上。
【解决方案6】:

Java 的日期/时间 API 使用起来非常糟糕。 java.util.Date 有几个构造函数来为特定日期创建实例,但它们都已被弃用。应该使用 java.util.GregorianCalendar 来代替,但这有一种非常烦人的设置字段的方式(想想 calendar.setField(GregorianCalendar.MONTH, 7) 而不是 calendar.setMonth(7) 会好得多)。画龙点睛的是,大多数其他类和库仍然需要 Date 而不是 Calendar,因此您必须不断地来回转换。

【讨论】:

  • 虽然日期/时间出现在一个代码中的所有地方,但如果您忽略不推荐使用的方法并制作防御性副本并忽略奇怪的类型层次结构,这并不是一个大问题。如果您忽略防御性副本并开始使用已弃用的 API 到处乱逛,那您就是在自找麻烦。但是日期时间等只是一些值类型,它们在功能上并不像 myexample 中使用的一个冰这样的大型库那样丰富。处理讨厌的日期是一次几行,但令牌流中的 Lucene 属性会涉及更多。
【解决方案7】:

不是赢家,但值得一提;安卓。使用 Java 5 编程语言,但几乎没有任何 Java 5 语言功能。你得到的不是枚举,而是带有前缀或后缀的整数常量。

它不能完全决定它应该是面向对象的还是程序化的。显示对话框就是一个很好的例子。几个带有自定义整数 id 的回调来显示对对话框的调用,这闻起来像旧的 C API。然后你会得到一个带有链式方法的内部构建器类,这闻起来是最糟糕的那种过度架构的 OOP。

MotionEvent 类将 X 和 Y 坐标作为来自同一个辅助方法的绝对值和相对值。但是没有办法检查它当前持有什么样的坐标。

Android 肯定是一个混合包。

【讨论】:

  • Android prolly 使用整数而不是枚举,因为枚举是类并且会占用更多内存。
  • 是的,这是真的,这是官方的立场。但我发现它就像“没有人需要超过 640K RAM”一样愚蠢。在构建“现代”框架时,您应该为未来做好计划,而不是让过去迫使您做出糟糕的设计。它不像枚举那样占用太多内存。
  • 更重要的是,没有什么能阻止编译器将枚举转换为整数。
  • 过早的优化是“其他”程序员的祸根。
  • @mP:还有你自己。永远不要今天写代码,明天就不能调试。优化紧密循环通常是浪费时间,改为更改算法。
【解决方案8】:

我将把这个问题抛在脑后,为一个标准 API 非常难看的库命名一个漂亮的 API:OpenGL 的 Haskell 绑定。

原因如下:

  • 库不是将所有内容集中到少量的头文件中,而是在逻辑上组织成离散的模块,其内容与 OpenGL 规范的结构平行。这让浏览文档成为一种愉快的体验。

  • 成对的“开始/结束”函数被高阶过程取代。例如,而不是

    pushMatrix();
      doSomeStuff();
      doSomeMoreStuff();
    popMatrix();
    

    你会说

    preservingMatrix $ do
        doSomeStuff
        doSomeMoreStuff
    

    绑定的语法强制执行库的约定,而不是让您手动完成。这也适用于四边形、三角形、线条等的绘图图元。当然,所有这些都是异常安全的。

  • Getter 和 setter 被惯用的“StateVars”取代,使读写操作更加对称。

  • 由多态性和额外数据类型替换的多个函数版本。不是用两个浮点值调用glVertex2f,而是用Vertex2 GLFloat 类型的值调用vertex

参考资料:

【讨论】:

  • Haskell 中的 OpenGL 确实不错,但是普通的 OpenGL 这么丑的主要原因是 C 很丑。很难设计一个非常好的 C API。
  • @Zirfe - 你我不同意什么是丑陋的。设计一个不错的 C API 是很有可能的。这只是相当具有挑战性。
【解决方案9】:

Direct3D!

毫无疑问,Direct3D 5 之前的旧界面非常丑陋:

// GL code
glBegin (GL_TRIANGLES);  
  glVertex (0,0,0);  
  glVertex (1,1,0);  
  glVertex (2,0,0);  
glEnd (); 

// D3D code, tonnes of crap removed
v = &buffer.vertexes[0];  
v->x = 0; v->y = 0; v->z = 0;  
v++;  
v->x = 1; v->y = 1; v->z = 0;  
v++;  
v->x = 2; v->y = 0; v->z = 0;  
c = &buffer.commands;  
c->operation = DRAW_TRIANGLE;  
c->vertexes[0] = 0;  
c->vertexes[1] = 1;  
c->vertexes[2] = 2;  
IssueExecuteBuffer (buffer); 

现在还不错 - 只需要 Microsoft 10 版本就可以了...

【讨论】:

    【解决方案10】:

    我会说 MFC、ATL 和 WTL。所有这 3 个库都使用了过多的匈牙利表示法,无缘无故地重新定义数据类型(CString 一遍又一遍地重新定义),并且随着每个版本的 Visual Studio 发生了众所周知的变化。

    我喜欢 COM。早在 .NET 开发之前,它就提供了面向组件的体系结构。然而,COM 扩展到 DCOM、它的许多包装器(如 ATL)以及它普遍缺乏全面的文档使其成为我在工作中必须处理的最丑陋的 API。

    【讨论】:

      【解决方案11】:

      肯定不是最丑的。可能有很多,但 Flex 在地狱中占有特殊的位置。特别是 UIComponent,与 Sprite 相比,感觉就像用电锯削苹果。我相信通过使用更轻量级的对象和 mixin 风格的特性(类似于 Dojo 在 Javascript 端的工作方式),Flex 会得到很大改进。

      ECMAScript/Actionscript Date 类几乎是倒退和无用的。每当我需要做一些比在日志中添加时间戳更复杂的事情时,这一直是一种痛苦。他们需要更多的解析选项(例如,指定输入格式的能力)和更好的时间管理,如智能增量、便利功能等......

      C++ STL 库(和一般的模板),虽然显然很有用,但总是让人觉得很丑陋。虽然没有改进建议。他们工作。

      【讨论】:

        【解决方案12】:

        Oracle 的 ProC、ProAda、Pro*this-that-the-other 的东西。它们是 C、Ada 和 Fortran 的预处理器前端,我想,也许还有其他一些,可以让你将 SQL 塞进你的源代码。

        他们确实也有一个运行得更好、更灵活的库。

        (那是十多年前的事了,我不知道他们现在在做什么,虽然如果它仍然是一样的,我不会感到惊讶,以免破坏人们的代码。)

        【讨论】:

          【解决方案13】:

          嗯,大约 20 年前它是一个著名的库,但我认为最初的 btrieve 数据引擎编写的 api 是最糟糕的。几乎所有事情都通过一次调用,它的许多参数中的每一个都包含不同的值,具体取决于您实际执行的调用(一个参数是一个标志,告诉系统您是否要打开文件,关闭文件,搜索,插入等)。那时我喜欢 btrieve,但我花了很长时间制作一个好的抽象层。

          如果不将所有内容都集中在一个调用中,则可以轻松改进它。不仅这个调用很可怕,而且程序员还负责分配、传入和释放位置块...... btrieve 使用一些内存来跟踪打开的文件句柄、位置等。另一个改进是允许 ascii定义索引时要使用的文本。索引必须通过复杂的二进制表示来指定。

          最好的问候, 不要

          【讨论】:

          • 听起来类似于旧的 dos int 调用,其中许多函数由一个公共 int 访问,并且仅由 s 寄存器中的一个数字来区分。
          • 这就是我认为的模型。只是引擎懒得为控制块分配自己的内存这一事实让我发疯了。
          【解决方案14】:

          许多 CRT 库函数的名称很差或含糊不清,这可能是由于当时遗留的编码限制,因此人们需要经常使用 F1 键才能找到正确的函数并提供正确的参数。

          我使用 CRT 功能已经有一段时间了,但我仍然发现自己在 F1 上的成绩相当不错。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-13
            • 2013-03-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多