代码之家  ›  专栏  ›  技术社区  ›  kizzx2

API设计:“容错”是好事吗?

  •  5
  • kizzx2  · 技术社区  · 16 年前

    我综合了许多有用的答案,并提出了自己的答案 answer below


    例如,我正在编写一个API Foo 需要显式初始化和终止(应该是语言不可知的,但是我在这里使用C++

    class Foo
    {
    public:
        static void InitLibrary(int someMagicInputRequiredAtRuntime);
        static void TermLibrary(int someOtherInput);
    };
    

    Init 函数只能调用一次,如果用其他任何输入再次调用它,将造成严重破坏。

    把这件事告诉我的来电者最好的方法是什么?我可以想出两种方法:

    1. InitLibrary ,我 assert
    2. 内部 初始化库 ,我检查一些静态变量,如果我的lib已经初始化,我会自动中止。

    方法#1显然是显式的,而方法#2则使其更加友好。我认为方法#2可能有一个缺点,即我的调用者不会意识到 初始化库 不应该叫两次。

    每种方法的优缺点是什么?有没有更聪明的方法来颠覆这一切?

    someMagicInputRequiredAtRuntime ). 这并不局限于初始化/终止,而是在其他情况下,我应该选择引用和引用“容错”还是失败。

    9 回复  |  直到 6 年前
        1
  •  2
  •   supercat    16 年前

    如果你的程序不能达到预期的post条件,我建议你标记一个异常。如果有人调用您的init例程两次,并且第二次调用后的系统状态将与刚刚调用一次时的状态相同,那么可能不需要抛出异常。如果第二次调用后的系统状态与调用方的期望不匹配,则应引发异常。

    总的来说,我认为从国家的角度思考比从行动的角度思考更有帮助。打个比方,如果试图以“write new”的形式打开已经打开的文件,可能会失败,或者导致关闭或重新打开。它不应该简单地执行no操作,因为程序将期望写入一个空文件,该文件的创建时间与当前时间匹配。另一方面,尝试关闭已经关闭的文件通常不应被视为错误,因为希望关闭该文件。

    顺便说一句,提供一个可能引发异常的方法的“Try”版本通常很有帮助。例如,最好有一个Control.TryBeginInvoke可用于更新例程(如果线程安全控件属性发生更改,则属性处理程序希望在控件仍然存在时对其进行更新,但实际上并不介意控件是否被释放;如果控件在更新其属性时被关闭,则无法避免第一次出现异常,这有点令人讨厌。

        2
  •  9
  •   Tomas Aschan    16 年前

    我一定会去的 方法1 一个容易理解的例外 那个

    另一方面,静默失败会导致用户认为第二次调用成功(没有错误消息,没有异常),因此他们会期望设置新的值。所以当他们想对你做些别的事情的时候 Foo

        3
  •  4
  •   Larry Watanabe    16 年前

         SA,  grant me the assertions 
         to accept the things devs cannot change 
         the code to except the things they can, 
         and the conditionals to detect the difference
    

    如果故障在环境中,那么您应该尝试让您的代码处理它。如果开发人员可以通过修复他们的代码来阻止它,那么它应该生成一个异常。

        4
  •  3
  •   Björn Pollex    16 年前

    一个好的方法是使用一个工厂来创建一个初始化的库对象(这需要将库包装到一个类中)。对工厂的多次create调用将创建不同的对象。往这边走 initialize -方法将不再是库的公共接口的一部分,工厂将管理初始化。

    singleton .

        5
  •  1
  •   Romain Hippeau    16 年前

    在类中有一个私有的静态计数器变量。如果它是0,则在Init中执行逻辑并递增计数器,如果它大于0,则简单地递增计数器。在术语中做相反的事情,递减直到它是0然后做逻辑。

    Singleton pattern

        6
  •  1
  •   kizzx2    16 年前

    我想打破这种困境的一个办法是实现这两个阵营。鲁比拥有 -w -Wall 甚至 -Weffc++

    在考虑了许多优秀的答案之后,我自己得出了这样一个结论:当有人坐下来时,我的API在理想情况下应该“正常工作”。当然,对于任何涉及任何领域的人来说,他都需要在比他试图解决的问题更低的一到两个抽象层次上工作,这意味着我的用户迟早要了解我的内部结构。如果他使用我的API足够长的时间,他将开始扩展限制,并且太多的努力来“隐藏”或“封装”内部工作只会变得麻烦。

    我想容错在大多数情况下都是一件好事,只是当API用户正在扩展一些小案例时,很难做到正确。我可以说,这两个世界中最好的是提供某种“严格模式”,这样当事情不“只是工作”,用户可以很容易地剖析问题。

    当然,做这件事需要很多额外的工作,所以我可能只是在这里谈理想。实际上,这一切都归结为具体的情况和程序员的决定。

        7
  •  0
  •   Svend    16 年前

    但是如果 someMagicInputRequiredAtRuntime 从一个调用到另一个调用,我会尽可能地引发错误,或者库可能不会按预期运行(“我用值42初始化lib,但它的行为就像我用11!?”初始化一样”)。

        8
  •  0
  •   Charles Bretana    16 年前

    如果这个库是一个静态类(没有状态的库类型),为什么不调用 Init
    大学教师;不允许公众访问 初始化 完全不起作用。

        9
  •  0
  •   deamon    16 年前

    我觉得你的界面有点太技术化了。没有程序员想知道您在设计API时使用了什么概念。程序员想要解决他们的实际问题,而不想学习如何使用API。没有人想初始化你的API,这是API应该尽可能在后台处理的事情。找到一个好的抽象概念,使开发人员尽可能避免接触低级的技术资料。这意味着API应该是容错的。