【问题标题】:Class Interfaces: Basic or Complex?类接口:基本还是复杂?
【发布时间】:2011-07-16 15:21:25
【问题描述】:

我正在编写一个用于娱乐和教育的容器类。以前在编写容器类时,我只使用了一些非常基本的方法:GetValueSetValueGetSizeResize。我这样做是为了避免“代码意大利面条”,这样我的课程会更容易调试。

但是,我想到该类的用户可能想要做的不仅仅是简单的替换。于是我又加了几个方法:

void Replace(const std::size_t Start, const std::size_t End, const T Value);
void Replace(const std::size_t Start, const std::size_t End, const MyClass Other);
void Insert(const std::size_t Index, const T Value);
void Insert(const std::size_t Index, const MyClass Other);
void Delete(const std::size_t Index);
void Delete(const std::size_t Start, const std::size_t End);

一般来说,类应该只提供最基本的接口,让类的用户自己编写函数来做复杂的事情吗?还是应该以可维护性为代价内置复杂的东西?

【问题讨论】:

  • 首先,您是否需要这些功能?其次,如果您遵循标准库模式,您的生活会更轻松。
  • @Chris 我的目的是教育和娱乐,所以可能是也可能不是。
  • 两种不同的插入和替换方法有何不同? T vs MyClass。
  • @mlaw Insert 将其他元素推开,为新元素腾出空间。 Replace 只是覆盖它们。容器类是模板化的,所以T 代表类存储的内容,MyClass 代表类本身。
  • 啊。知道了。所以 Insert(Index, MyClass Other) 就像一个“addAll”操作。

标签: c++ oop interface


【解决方案1】:

如果此容器仅由您在您的代码中使用,并且您的接口方法足以满足特定目的,那么这样做很好。

但是,一旦其他人要使用该容器,或者您打算在其他领域使用它,我建议添加与迭代器类型一起使用的接口方法,那么您的容器就可以更开放地与stdlib 容器和算法。以 stdlib 容器的接口为例。

【讨论】:

    【解决方案2】:

    我也遇到过类似的情况。我的建议是,你有 2 个“基类”或“超类”。

    第一个类,很笼统的类,代表所有容器类的“概念根”,几乎不是方法,类似于一个接口,应该是这样的:


    容器.hpp

    class Container
    {
    protected:
       int GetValue();
       void SetValue(int newValue);
    
       size_t GetSize();
    
       void Resize(size_t);
    };
    

    第二课,开始少了一点概念性,更“真实世界”:


    mcontainers.hpp

    #include "containers.hpp";
    
    class MethodContainer: public Container
    {
    protected:
      void Replace(const std::size_t Start, const std::size_t End, const T Value);
      void Replace(const std::size_t Start, const std::size_t End, const MyClass Other);
      void Insert(const std::size_t Index, const T Value);
      void Insert(const std::size_t Index, const MyClass Other);
      void Delete(const std::size_t Index);
      void Delete(const std::size_t Start, const std::size_t End);
    

    }

    最后,还有一些具体的类:


    堆栈.hpp

    #include "containers.hpp";
    #include "mcontainers.hpp";
    
    #define pointer void*
    
    class Stack: public MethodContainer
    {
    public:
      // these methods use "mcontainer::Insert", "mcontainer::Replace", etc
      void Push(pointer Item);
      void Pop();
      pointer Extract();
    }
    

    正如@Chris 提到的,有几个库可以做到这一点,但规则总是有一个例外,如果需要,您可能想要“重新发明轮子”。

    我有一个应用程序,它带有一组库,其中包括一些容器/集合。它是在另一个程序中制作的。朗格并需要将其迁移到 C++。 Altought,我还检查了 c++ 标准库,我结束了将库迁移到 C++,因为我有几个库调用我的容器库,并且需要它来快速完成。

    使用“基类”时,您可能希望在子类中“保护”其成员,并“将其公之于众”。除非必要,否则我通常不会创建“私有”字段或方法。

    总结:一些非常常见的复杂事情(如内存分配或存储)可能会在您的基类中完成,但是,这些复杂性中的大部分应该留给子类。

    【讨论】:

      【解决方案3】:

      类应该只提供成员函数的基本/最小接口(最好不要提供数据!)。然后,您可以将便捷方法添加为 non-friend non-member 函数。然而,根据接口原理,这些函数仍然是类接口的一部分。

      您已经指出了这样做的主要原因:它使类更易于维护。另外,实现你的“convienence”方法部分将作为一个很好的测试来看看你的接口是否足够好。

      请注意,容器的成员函数部分通常应该非常通用且功能强大,并且只关心维护类不变量。

      据我所知,这是关于该主题的最现代观点。它在 Scott Meyer 的“Effective C++”(在最近的第 3 版中)以及 Sutter 和 Alexandrescu 的“C++ 编码标准”中得到了突出的支持。

      【讨论】:

        【解决方案4】:

        问题是,一旦你编写了另一个容器类(野外有很多,你可能需要不同的种类),你会发现你的设计正方形在 O(N * M) 中,其中 N 是容器类的数量和 M 算法的数量。

        解决方案是将容器与算法分离,这就是为什么在 STL 中引入迭代器的原因。

        有迭代器的替代品,例如使用。多态性。您可以在抽象的公共基类中分解出遍历接口,并根据它实现算法。

        简而言之,将大部分逻辑排除在容器类之外。

        【讨论】:

          【解决方案5】:

          您应该尽量保持界面简洁,特别是如果您可能想要实现不同的容器类型,例如基于数组和链表。如果您在所有容器中提供一些基本方法,则可以创建执行某些任务但可以在所有容器上工作的外部算法:

           void Replace(const std::size_t Start, const std::size_t End, const T Value);
          

          可以变成

           template<class ContainerType>
           void ReplaceAllElementsInContainer(ContainerType& Container, const std::size_t Start, const std::size_t End, const T Value);
          

          课外。如果不这样做,则必须在所有容器中编写所有这些方法。

          另一种可能性是使用模板方法模式(与 C++ 模板无关)并将所有这些方法编写在一个基类中(它将基本方法定义为纯虚拟并从实现的“便利”方法中调用它们)。这可能会导致许多虚函数调用,出于性能原因,容器类中可能不需要这些调用。

          【讨论】:

            猜你喜欢
            • 2012-06-30
            • 2020-01-17
            • 1970-01-01
            • 2010-10-11
            • 1970-01-01
            • 1970-01-01
            • 2014-11-06
            • 1970-01-01
            • 2012-09-12
            相关资源
            最近更新 更多