导出&破坏封装,还是违反 DRY?

发布于 2024-11-18 00:53:40 字数 1195 浏览 3 评论 0原文

我有两个 C++ 类:Sequence,它就像 std::vectorFile,它是一个 Sequence code> 代表计算机上文件的字符串。

Sequence 派生File 是理所当然的事情。它的行为完全相同,但增加了读取和写入文件的功能。特定于File的功能可以轻松实现,无需将Sequence的数据成员标记为受保护。相反,它们可以是私有的,并且 File 可以使用 Sequence 的公共接口。到处都是快乐的时光。

我想创建一个在内部管理动态分配内存的 Array 类。 Array 对象无法调整大小;大小是在构造函数中指定的。*

这是事情变得有争议的地方。

从概念上讲,从 Array 派生 Sequence 是有意义的。正如 File 是一个具有读写文件附加功能的 Sequence 一样,Sequence 是一个具有以下功能的 Array:按需调整大小的附加功能。

但有一个关键的区别:调整大小函数需要直接访问Array正在管理的内存。换句话说,以前的私有成员现在必须受到保护。

使用受保护的成员而不是私有成员会破坏封装。 ArraySequence 之间的链接是唯一需要它的链接;作品中的其他类只能使用其父级的公共接口。从这个意义上说,推导是一个坏主意。

您可能会说,想要数组的人可以只使用 Sequence 并忽略调整大小功能。但话又说回来,您可以只使用 File 并忽略读/写功能。这就像购买一台笔记本电脑但从未将其从办公桌上移走一样。这根本没有意义。

最好的举措是什么:派生并可能破坏封装;使 Array 成为一个完全独立的类,并且必须毫无意义地重新实现许多功能;或者完全忘记Array而只是让人们使用Sequence

*请注意,这是一个娱乐和教育项目,因此拥有不可调整大小的动态分配数组的实用性不是重点。

I have two C++ classes: Sequence, which is just like std::vector, and File, which is a Sequence of strings that represents a file on the machine.

Deriving File from Sequence is a no-brainer. Its behavior is exactly the same, but with the added functionality of reading and writing files. The File-specific functionality is implemented easily, without the need for Sequence's data members to be marked as protected. Instead, they can be private, and File can use Sequence's public interface. Happy times all around.

I want to make an Array class that internally manages dynamically-allocated memory. An Array object cannot be resized; the size is specified in the constructor.*

Here's where things get arguable.

Concept-wise, it makes sense to derive Sequence from Array. Just as a File is a Sequence with the added functionality of reading and writing files, Sequence is an Array with the added functionality of resizing on-demand.

But there's a key difference: The resizing functions require direct access to the memory Array is managing. In other words, the previously-private members must now be protected.

Using protected members instead of private ones destroys encapsulation. The link between Array and Sequence is the only one that requires it; other classes in the works can just use their parents' public interfaces. In this sense, it's a bad idea to derive.

You could argue that people who want arrays can just use Sequence and ignore the resizing functionality. But then again, you could just use File and ignore the read/write functionality. It would be like buying a laptop but never moving it from your desk. It simply doesn't make sense.

What's the best move: To derive, and potentially destroy encapsulation; to make Array a completely free-standing class, and have to pointlessly re-implement a lot of functionality; or to forget about Array completely and just make people use Sequence?

*Note that this is a project for fun and education, so the practicality of having a non-resizable dynamically-allocated array is beside the point.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自演自醉 2024-11-25 00:53:40

您可能会考虑从稍微不同的方向来解决问题。也许这个问题可以用模板来解决,而不是继承,具体来说,是管理集合缓冲区的策略模板。您(至少)有两种实现:一种用于固定分配,另一种用于自动调整大小。

这根本不会破坏封装,而且我可以看到两者之间几乎唯一的重叠是初始分配可能大致相同,无论是固定的还是可变的。鉴于这是多么微不足道的事情,我怀疑是否值得花费大量时间或精力来尝试将其分解出来。理论上可能是这样,但至少在典型情况下,我们谈论的是一行代码,而且是相当简单的代码。

暂时回到继承问题,归结为:这非常类似于标准圆形与椭圆形的情况,其中一个有足够的相似性,使一个看起来像另一个,但最终都不满足 LSP - 你不能安全地将其中一个视为另一个,因此(至少是公共)继承不合适。私有继承不需要遵循 LSP,但通常仅在您需要/想要重写基类的虚函数时才有用,这在这里似乎也不太可能。

You might consider slicing the problem in a slightly different direction. Instead of inheritance, perhaps this problem could be solved with a template -- specifically, a policy template that manages a buffer for a collection. You'd have (at least) two implementations of that: one for fixed allocation, the other for automatically resizing.

That wouldn't break encapsulation at all, and nearly the only overlap I can see between the two would be that the initial allocation is probably about the same whether fixed or variable. Given how trivial that is, I doubt it's worth spending much time or effort on trying to factor it out. In theory it could be, but at least in a typical case we're talking about one line of code, and a pretty simple one at that.

Going back to the inheritance question for a moment, it comes down to this: this is very much like the standard circle vs. oval situation, where there's enough similarity for one to seem like the other, but ultimately neither satisfies the LSP -- you can't treat either one as the other safely, so (at least public) inheritance isn't suitable. Private inheritance doesn't require following LSP, but is generally only useful when/if you need/want to override a base class' virtual function, which seems unlikely here as well.

故人如初 2024-11-25 00:53:40

我不会在这里使用推导。

Sequence 并不是真正的Array。虽然实际上它们似乎有许多常见的方法,但从设计的角度来看,它们的用途和保证却截然不同。

不过,在 Sequence 中使用 Array 是有意义的,并且让 Sequence 将许多调用(内联)转发到直接 Array

template <typename T>
class Sequence
{
public:
  Sequence(): _array(10) {}
  explicit Sequence(size_t n): _array(n) {}

  bool empty() const { return _size == 0; }
  size_t size() const { return _size; }
  size_t capacity() const { return _array.size(); }

private:
  size_t _size; // current size
  Array<T> _array;
}; // class Sequence

注意:我在这里假设 Array 是一次性构建所有元素的,而序列会一次添加一个元素

同样,这是否有意义对于从 a 派生的 File 序列?您是否有实施问题,例如将 Sequence 的内容与磁盘上的表示同步?

I would not use derivation here.

A Sequence is not really an Array. While practically they appear to have a number of common methods, from a design point of view they have very different uses and guarantees.

It would make sense, though, to use an Array within the Sequence, and for Sequence to forward a number of calls (inline) to the Array directly:

template <typename T>
class Sequence
{
public:
  Sequence(): _array(10) {}
  explicit Sequence(size_t n): _array(n) {}

  bool empty() const { return _size == 0; }
  size_t size() const { return _size; }
  size_t capacity() const { return _array.size(); }

private:
  size_t _size; // current size
  Array<T> _array;
}; // class Sequence

Note: I assumed here that the Array was built with all its elements at once, while the sequence will add them one at a time

Similarly, does it make sense for a File to derive from a Sequence ? Don't you have implementation issues, like sync'ing the content of Sequence with the on-disk representation ?

非要怀念 2024-11-25 00:53:40

好吧,在您的情况下,使用 public 继承从 Array 派生 Sequence 绝对是个坏主意(就像从矩形派生正方形一样)。用面向对象编程的术语来说,序列不是数组,因为Array具有Sequence所没有的属性,它是: 数组对象无法调整大小。如果您进行推导,它将打破里氏替换原则

在您的情况下,当您想要实现另一个类中已经存在的某些功能时,我建议您使用 private 继承(这意味着实现的继承)或 composition,例如,将Array 的实例存储在Sequence 的私有区域中,并将其用于内部实现。

UPD:但是,在我看来,使用 Array 实现 Sequence 似乎也是有问题的。也许最好创建一些抽象基类 Container 来实现 SequenceArray 的常见功能,然后派生这两个类从中学习课程。

Well, deriving Sequence from Array with public inheritance in your case is definitely bad idea (as deriving square from rectangle). In the terms of Object-Oriented Programming, Sequence IS NOT an Array, since Array has a property that Sequence does not have, and it's: An Array object cannot be resized. If you make a derivation, it will break the Liskov substitution principle.

In your case, as you want to implement some functionality, already existing in another class, I would suggest you to use either private inheritance (which means inheritance of implementation), or composition, e.g. storing an instance of Array in private zone of Sequence and using it for inner implementation.

UPD: However, implementing Sequence with usage of an Array also seems to me quite problematic. Maybe it would by much better to create some abstract base class Container that would implement the common functionality of Sequence and Array, and then derive both these classes from it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文