[英]Derive & Destroy Encapsulation, or Violate DRY?
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.我有两个 C++ 类: Sequence
,就像std::vector
和File
,它是代表机器上文件的字符串Sequence
。
Deriving File
from Sequence
is a no-brainer.从Sequence
派生File
是不费吹灰之力的。 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. File
特定的功能很容易实现,无需将Sequence
的数据成员标记为受保护。 Instead, they can be private, and File
can use Sequence
's public interface.相反,它们可以是私有的,并且File
可以使用Sequence
的公共接口。 Happy times all around.到处都是快乐的时光。
I want to make an Array
class that internally manages dynamically-allocated memory.我想制作一个内部管理动态分配的 memory 的Array
class。 An Array
object cannot be resized;无法调整Array
object 的大小; the size is specified in the constructor.*大小在构造函数中指定。*
Here's where things get arguable.这就是事情变得有争议的地方。
Concept-wise, it makes sense to derive Sequence
from Array
.从概念上讲,从Array
派生Sequence
是有意义的。 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.就像File
是一个具有读取和写入文件的附加功能的Sequence
Sequence
一个具有按需调整大小的附加功能的Array
。
But there's a key difference: The resizing functions require direct access to the memory Array
is managing.但有一个关键区别:调整大小功能需要直接访问正在管理的 memory Array
。 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; Array
和Sequence
之间的链接是唯一需要它的链接; 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.您可能会争辩说,想要 arrays 的人可以只使用Sequence
并忽略调整大小的功能。 But then again, you could just use File
and ignore the read/write functionality.但是话又说回来,您可以只使用File
并忽略读/写功能。 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;使Array
成为一个完全独立的 class,并且必须毫无意义地重新实现很多功能; or to forget about Array
completely and just make people use Sequence
?还是完全忘记Array
而只是让人们使用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. *请注意,这是一个娱乐和教育项目,因此具有不可调整大小的动态分配数组的实用性无关紧要。
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.而不是 inheritance,也许这个问题可以用一个模板来解决——具体来说,一个管理集合缓冲区的策略模板。 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.暂时回到 inheritance 问题,归结为:这非常类似于标准圆形与椭圆形的情况,其中一个看起来像另一个,但最终两者都不满足 LSP - 你不能安全地将任何一个视为另一个,因此(至少是公开的)inheritance 不适合。 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.私有 inheritance 不需要遵循 LSP,但通常仅在/如果您需要/想要覆盖基类的虚拟 function 时有用,这在这里似乎也不太可能。
I would not use derivation here.我不会在这里使用推导。
A Sequence
is not really an Array
. Sequence
并不是真正的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:但是,在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
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注意:我在这里假设 Array 是同时使用其所有元素构建的,而序列将一次添加一个
Similarly, does it make sense for a File
to derive from a Sequence
?同样, File
从Sequence
派生是否有意义? Don't you have implementation issues, like sync'ing the content of Sequence
with the on-disk representation?您是否没有实现问题,例如将Sequence
的内容与磁盘表示同步?
Well, deriving Sequence
from Array
with public inheritance in your case is definitely bad idea (as deriving square from rectangle).好吧,在您的情况下,使用公共inheritance 从Array
派生Sequence
绝对是个坏主意(从矩形派生正方形)。 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
.就面向对象编程而言,Sequence IS NOT an Array,因为Array
具有Sequence
所没有的属性,它是: An Array object cannot be resized
。 If you make a derivation, it will break the Liskov substitution principle .如果进行推导,就会破坏Liskov 替换原则。
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 , eg storing an instance of Array
in private zone of Sequence
and using it for inner implementation.在您的情况下,由于您想要实现一些功能,已经存在于另一个 class 中,我建议您使用私有inheritance (这意味着 inheritance 的实现),或存储Sequence
的私有区域组合和例如Array
将其用于内部实现。
UPD: However, implementing Sequence
with usage of an Array
also seems to me quite problematic. UPD:但是,在我看来,使用Array
实现Sequence
也很成问题。 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.也许最好创建一些抽象的基础 class Container
来实现Sequence
和Array
的通用功能,然后从中派生这两个类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.