导出&破坏封装,还是违反 DRY?
我有两个 C++ 类:Sequence
,它就像 std::vector
和 File
,它是一个 Sequence
code> 代表计算机上文件的字符串。
从Sequence
派生File
是理所当然的事情。它的行为完全相同,但增加了读取和写入文件的功能。特定于File
的功能可以轻松实现,无需将Sequence
的数据成员标记为受保护。相反,它们可以是私有的,并且 File
可以使用 Sequence
的公共接口。到处都是快乐的时光。
我想创建一个在内部管理动态分配内存的 Array 类。 Array
对象无法调整大小;大小是在构造函数中指定的。*
这是事情变得有争议的地方。
从概念上讲,从 Array
派生 Sequence
是有意义的。正如 File
是一个具有读写文件附加功能的 Sequence
一样,Sequence
是一个具有以下功能的 Array
:按需调整大小的附加功能。
但有一个关键的区别:调整大小函数需要直接访问Array
正在管理的内存。换句话说,以前的私有成员现在必须受到保护。
使用受保护的成员而不是私有成员会破坏封装。 Array
和 Sequence
之间的链接是唯一需要它的链接;作品中的其他类只能使用其父级的公共接口。从这个意义上说,推导是一个坏主意。
您可能会说,想要数组的人可以只使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可能会考虑从稍微不同的方向来解决问题。也许这个问题可以用模板来解决,而不是继承,具体来说,是管理集合缓冲区的策略模板。您(至少)有两种实现:一种用于固定分配,另一种用于自动调整大小。
这根本不会破坏封装,而且我可以看到两者之间几乎唯一的重叠是初始分配可能大致相同,无论是固定的还是可变的。鉴于这是多么微不足道的事情,我怀疑是否值得花费大量时间或精力来尝试将其分解出来。理论上可能是这样,但至少在典型情况下,我们谈论的是一行代码,而且是相当简单的代码。
暂时回到继承问题,归结为:这非常类似于标准圆形与椭圆形的情况,其中一个有足够的相似性,使一个看起来像另一个,但最终都不满足 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.
我不会在这里使用推导。
Sequence
并不是真正的Array
。虽然实际上它们似乎有许多常见的方法,但从设计的角度来看,它们的用途和保证却截然不同。不过,在
Sequence
中使用Array
是有意义的,并且让Sequence
将许多调用(内联)转发到直接Array
:注意:我在这里假设 Array 是一次性构建所有元素的,而序列会一次添加一个元素
同样,这是否有意义对于从 a 派生的
File
序列
?您是否有实施问题,例如将Sequence
的内容与磁盘上的表示同步?I would not use derivation here.
A
Sequence
is not really anArray
. 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 theSequence
, and forSequence
to forward a number of calls (inline) to theArray
directly: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 aSequence
? Don't you have implementation issues, like sync'ing the content ofSequence
with the on-disk representation ?好吧,在您的情况下,使用 public 继承从
Array
派生Sequence
绝对是个坏主意(就像从矩形派生正方形一样)。用面向对象编程的术语来说,序列不是数组,因为Array
具有Sequence
所没有的属性,它是:数组对象无法调整大小
。如果您进行推导,它将打破里氏替换原则。在您的情况下,当您想要实现另一个类中已经存在的某些功能时,我建议您使用 private 继承(这意味着实现的继承)或 composition,例如,将
Array
的实例存储在Sequence
的私有区域中,并将其用于内部实现。UPD:但是,在我看来,使用
Array
实现Sequence
似乎也是有问题的。也许最好创建一些抽象基类Container
来实现Sequence
和Array
的常见功能,然后派生这两个类从中学习课程。Well, deriving
Sequence
fromArray
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, sinceArray
has a property thatSequence
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 ofSequence
and using it for inner implementation.UPD: However, implementing
Sequence
with usage of anArray
also seems to me quite problematic. Maybe it would by much better to create some abstract base classContainer
that would implement the common functionality ofSequence
andArray
, and then derive both these classes from it.