【问题标题】:Documenting interfaces [closed]记录接口[关闭]
【发布时间】:2011-06-05 19:49:54
【问题描述】:

我想知道如何在我的应用程序中记录一个接口IResource。由于我编写的是引擎而不是库,因此我认为文档应该提供有关如何编写接口实现的指南;可以吗?

另外,请您看看我的界面,告诉我cmets是否足够清晰?

/**
    Interface that should be implemented by all resources. Implementing
    this interface is necessary for compatibility with the ResourceManager
    class template.

    \note Documentation of this interface includes guidelines on how
    implementations should be written.

    \see ResourceManager
                                                                              */
class IResource
{
  public:
    /**
        Loads resource data from a file. If data is already loaded,
        the function should return immediately.

        \throw std::exception Should throw on any failure to load the
        resource. If the resource is already loaded, don't throw, just
        return (as previously indicated).

        \note Access to this function should also be provided directly
        from a constructor. That constructor should catch any exceptions
        and throw them further to its caller.
                                                                              */
    virtual void loadFromFile(const std::string& file) = 0 ;
    /**
        All general guidelines from loadFromFile() also apply to this
        function. Additionally, the resource should not take possession of
        the buffer; the buffer should be safe to delete after loading.
                                                                              */
    virtual void loadFromMemory(const char* buffer, std::size_t size) = 0;
    /**
        Frees the data currently held by the resource object. Should
        return immeditelly if no data is loaded.
                                                                              */
    virtual void free() = 0;
    virtual bool isLoaded() const = 0;
};

编辑:开启了相关讨论。

主要遵循Johann Gerell's answer 的cmets 部分的对话,我在programmers.stackexchange 上打开了一个相当长的线程。你可以在这里查看:
> Single-responsibility and custom data types

【问题讨论】:

  • 你知道不能在构造函数中调用虚方法吗?
  • 显然你不会在构造函数中调用IResource::loadFromFile()。例如,在其构造函数中,Font 将调用它自己的该函数的实现,它编译得很好。
  • 如果您从 Font 派生并编写新版本会怎样。那么 Font(s) 构造函数中的版本将不会调用正确的版本。阅读下面的@Johann Gerell,了解将loadingbeing 资源划分为不同的类。
  • @Martin 你的意思是这样吗? ideone.com/ni9u3 似乎工作正常。
  • @Martin:不能在基类构造函数中调用虚函数。从派生类构造函数中,它工作得很好。

标签: c++ interface documentation


【解决方案1】:

您已经很好地记录了意图,这是一个非常好的开始。

缺少一些东西:

  • 您没有记录论点。它们是不言而喻的,但我可能有点迂腐(doxygen 也是如此)。
  • isLoaded 有什么作用?
  • 关闭 doxygen 中的继承文档功能。虽然您的 cmets 对接口类有效,但它们对实现该接口的某些类无效。

【讨论】:

  • 我同意。令人惊讶的是,在我写完它六个月后,显而易见的事情是多么不明显;-) 最好记录一些可能不需要记录的东西,而不是不记录它,然后希望有人拥有它!
【解决方案2】:

您的 cmets 应指定用户和实施者之间的合同;没有必要关注任何一方的观点。

关于获得缓冲区所有权的指导听起来不像是建议,而是明确的要求。使用“will”:所有对缓冲区的访问都将在此函数返回之前执行;那时缓冲区可能会被安全地释放。类似地:如果资源已经被加载,这个函数会立即返回。这是一个要求,而不是一个建议。

应完整记录参数。文件名可以是相对路径吗?如果文件不存在会怎样?由于权限不可读?

您应该描述可能引发哪些异常。捕获和重新抛出异常只是糟糕的设计。

对非字符数据使用const char* 是不好的设计,请改用const void*。记录大小以字节为单位(如果不是,单位是什么)。数据是任何通用格式,还是子类独有的?

保留特定于该功能的每个功能的文档。有关多个函数之间的交互的信息,或有关派生类的构造函数行为的建议,属于类级别。

使用正确的拼写,拼写错误的单词会分散内容的注意力。

【讨论】:

  • 你的意思是我应该记录哪些异常可能被抛出?我没有任何特殊的异常类,我只使用std::exception。错误消息将发送到 Logger,用户可以在那里阅读。
  • 关于const char*:我实际上想用const int8* 之类的东西来代替它,只是还没有做到——我还应该使用const void*吗?
  • @Paul:为什么您的界面要为主应用程序做出错误处理策略决策?它应该为主程序提供足够的信息来实现有用的策略,无论是记录、重试还是只向用户显示某些类型的错误。对每个错误使用std::exception 对此无济于事。有一个异常层次结构是有原因的。
  • @Paul:接口应该使用const void*。该实现可以使用const int*RGBTRIPLE* 或任何对处理数据最有意义的数据类型。
  • Ben 对合同的评论恰如其分。 Paul,您在这里针对两个受众:通过调用其中一个纯虚拟方法(简称“用户”)来使用此类的人,以及实现从此类派生的类的人(简称“实施者”) .用户需要抽象地知道那些纯虚拟应该做什么/允许做什么/禁止做什么。实施者也需要知道这一点,但从稍微不同的角度来看。
【解决方案3】:

考虑到您列表的代码行为而不是文档部分,我认为您应该根据Single Responsibility Principle 和@987654322 重新考虑一下界面(文档上的其他 cmets 已经足够好了) @。现在,IResource 的名称暗示该类型具有资源行为,但我认为它根本没有这种行为。想想你将如何使用这种类型。最合乎逻辑的是,您会传递指向IResource 的引用或指针。然后,在对它进行任何操作之前,您需要检查它是否已使用 isLoaded 加载。总是。

如果您将加载资源与成为资源的两个职责分开会怎样:

class IResource
{
public:
    virtual SomeCommonResourceBehavior(...) = 0;

    virtual ~IResource() {}
};

class IResourceFactory
{
public:
    virtual std::unique_ptr<IResource> CreateFromFile(...) = 0;
    virtual std::unique_ptr<IResource> CreateFromMemory(...) = 0;

    virtual ~IResourceFactory() {}
};

这样,当您在代码中的任何位置看到指向 IResource 的引用或非空指针时,您就知道它已经创建了。

另外,如果您无法在IResource 中识别出任何SomeCommonResourceBehavior,那么您可能认为您的设计有点错误。

编辑:如果您生活在 C++0x 之前的地区,那么 boost::unique_ptr&lt;&gt; 是工厂中的替代方案。如果 boost 不是替代方案,std::auto_ptr&lt;&gt; 比原始指针更好。

【讨论】:

  • 我已经有类似工厂的东西:ResourceManager。即便如此,如果发生故障,工厂/经理仍必须返回一些东西(即空指针),您仍然需要检查,对吧?我也考虑过完全分离加载,但我认为我应该复制标准对象的行为和设计(例如,std::ifstream,它具有openclose 函数)。
  • @Paul:两个错误不等于一个正确。为什么要抄袭std::ifstream的设计?你需要openclose吗?您是否会对未加载的 IResource 有任何用处?如果是这种情况,那么它可能甚至不应该首先被分配。如果您养成了让对象已创建且可用而不是已创建且可能可用的习惯,您将为自己省去很多麻烦、不必要的代码路径和增加的复杂性。
  • 没有人投票结束这个问题,我们投票把它移到programmers.se,因为这个问题是关于软件开发过程,而不是关于代码的行为。
  • @Ben:啊,我明白了。我只看了“close (2)”字符串。
  • 这需要更复杂的讨论。我在programmers.se开了一个新帖子Single-responsibility and custom data types
猜你喜欢
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 2012-04-17
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
相关资源
最近更新 更多