简体   繁体   English

类接口:基本还是复杂?

[英]Class Interfaces: Basic or Complex?

I'm writing a container class for fun and education. 我正在写一个充满乐趣和教育的容器类。 Previously when writing container classes I had limited myself to just a few very basic methods: GetValue , SetValue , GetSize and Resize . 以前在编写容器类时,我只限于几个非常基本的方法: GetValueSetValueGetSizeResize I did this to avoid "code spaghetti" so my class would be easier to debug. 我这样做是为了避免“代码意大利面条”,所以我的课程将更容易调试。

However, it occurred to me that users of the class might want to do more than just simple substitution. 但是,在我看来,类的用户可能想要做的不仅仅是简单的替换。 So I added a few more methods: 所以我添加了一些方法:

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);

In general, should classes provide only the most basic interface and let the users of the class make their own functions to do the complex stuff? 一般来说,类应该只提供最基本的接口,让类的用户自己做功能来做复杂的事情吗? Or should the complex stuff be built-in at the cost of maintainability? 或者是否应该以可维护性为代价内置复杂的东西?

Classes should only provide a basic/minimal interface of member-functions (and preferably no data!). 类应该只提供成员函数的基本/最小接口(最好是没有数据!)。 You can then add convenience methods as non-friend non-member functions. 然后,您可以将便捷方法添加为非朋友非成员函数。 According to the interface-principle however, these functions are still part of your classes interface. 但是,根据接口原理,这些函数仍然是类接口的一部分。

You already named the prime reason for this: it makes the class a lot easier to maintain. 您已经为此命名了主要原因:它使类更容易维护。 Also, implementing your "convienence" method part will serve as a nice test to see if you interface is good enough. 此外,实施“convienence”方法部分将是一个很好的测试,看看你的界面是否足够好。

Note that the member function part of a container should usually be very generic and powerful, and care not about much more than maintaining class invariants. 请注意,容器的成员函数部分通常应该非常通用且功能强大,而不仅仅是维护类不变量。

This is most modern opinion on the subject, as far as I know. 据我所知,这是关于这个问题的最现代意见。 It is prominently advocated in Scott Meyer's "Effective C++" (In the most recent 3rd edition) and in Sutter's and Alexandrescu's "C++ Coding Standards". 它在Scott Meyer的“Effective C ++”(最新的第3版)以及Sutter和Alexandrescu的“C ++编码标准”中得到了突出的提倡。

The problem is that as soon as you write another container class (there are plenty of them in the wild, you may need different kinds), you will find that your design squares in O(N * M), where N is the number of container classes and M the number of algorithms. 问题是,只要你编写另一个容器类(在野外有很多容器类,你可能需要不同类型),你会发现你的设计方格在O(N * M)中,其中N是数字容器类和M算法的数量。

The solution is to decouple the containers from the algorithms, and this is why iterators were introduced in the STL. 解决方案是将容器与算法分离,这就是在STL中引入迭代器的原因。

There are alternatives to iterators, using eg. 迭代器有替代品,例如使用。 polymorphism. 多态性。 You can factor out the traversing interface in an abstract common base class, and implement the algorithms in term of it. 您可以在抽象公共基类中分解遍历接口,并根据它来实现算法。

In short, keep the most logic out of your container classes. 简而言之,保持容器类中最多的逻辑。

You should try to keep your interface lean, especially if you might want to implement different container types, eg array-based and linked-list. 您应该尝试保持您的界面精简,特别是如果您可能希望实现不同的容器类型,例如基于阵列和链接列表。 If you provide some basic methods in all your containers, you can create external algorithms which perform certain tasks but which can work on all containers: 如果在所有容器中提供一些基本方法,则可以创建执行某些任务但可以在所有容器上运行的外部算法:

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

could become 可能成为

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

outside the classes. 课外。 If you don't do this, you have to write all those methods In all your containers. 如果不这样做,则必须在所有容器中编写所有这些方法。

Another possibility is to use the template method pattern (not related to C++ templates) and write all these methods in a base class (which defines the basic methods as pure virtual and calls them from the implemented "convenience" methods). 另一种可能性是使用模板方法模式(与C ++模板无关)并将所有这些方法写入基类(将基本方法定义为纯虚拟,并从实现的“方便”方法中调用它们)。 This leads to possibly many virtual function calls which might not be desired in a container class for performance reasons. 这导致可能存在许多虚拟函数调用,出于性能原因,这些调用可能在容器类中是不期望的。

I had a similar case like these. 我有类似这样的案例。 My suggestion is, that you have 2 "base classes" or "super classes". 我的建议是,你有2个“基类”或“超类”。

The first class, very general class, represents the "conceptual root" for all containers classes, almost not methods, similar to an interface, and should be like: 第一个类,非常通用的类,代表所有容器类的“概念根”,几乎不是类似于接口的方法,应该是:


containers.hpp containers.hpp

class Container
{
protected:
   int GetValue();
   void SetValue(int newValue);

   size_t GetSize();

   void Resize(size_t);
};

The second class, starts to be a little less conceptual and more "real world": 第二类,开始变得不那么概念化,更“现实世界”:


mcontainers.hpp 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);

} }

And, finally, some classes that are concrete: 最后,一些具体的类:


stacks.hpp stacks.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();
}

AS @Chris mentioned, there are several libraries for doing this, but there is always an exception to the rule, and you may want to "reinvent the wheel" if you need it. AS @Chris提到,有几个库用于执行此操作,但规则总是有例外,如果需要,您可能需要“重新发明轮子”。

I had an application, with its set of libraries, that included some containers / collections. 我有一个应用程序,它有一组库,包括一些容器/集合。 It was made in another progr. 它是在另一个程序中制作的。 langr. langr。 and need it to migrate it to C++. 并需要它将其迁移到C ++。 Altought, I also check the c++ standard libraries, I ended migrating my libraries to C++, because I had several libraries calling my container libraries, and need it to do it fast. Altought,我也检查了c ++标准库,我结束了将我的库迁移到C ++,因为我有几个库调用我的容器库,需要它快速完成。

When using "base classes", you may want to "protect" its members, and "take it to the public", in child classes. 使用“基类”时,您可能希望“保护”其成员,并在子类中“将其带到公众”。 I usually don't make "private" fields or methods, unless necessarily. 除非必要,否则我通常不会制作“私人”字段或方法。

Summary: Some very common complex stuff (like memory allocation or storage) may be done in your base classes, but, most of those complexity should be leave it to child classes. 简介:一些非常常见的复杂内容(如内存分配或存储)可能在您的基类中完成,但是,大多数复杂性应该留给子类。

If this container is used only by you in your code and the methods of your interface are sufficient for a specific purpose it is fine doing it this way. 如果您的代码中只有您使用此容器,并且接口的方法足以满足特定目的,那么就可以这样做。

However, as soon as someone else is going to use the container, or you plan to use it in other areas I'd recommend adding interface methods working with iterator types, then your container is much more open to be used with the stdlib containers and algorithms. 但是,只要其他人打算使用容器,或者您计划在其他区域使用它,我建议添加使用迭代器类型的接口方法,那么您的容器更加开放,可以与stdlib容器一起使用,算法。 Use the interfaces of the stdlib containers as example. 以stdlib容器的接口为例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM