我有 A 和 B 类。现在我需要编写一个新的 C 类,它将使用 A 和 B 中的一些字段和方法,但不是全部。(我将使用 A 和 B 中大约 50% 的东西)。
现在我正在考虑从 A 和 B 继承。但这会使 C 包含很多没有意义的字段和方法。
基本上我只是为了代码重用的目的而使用继承,否则我将不得不从 A 和 B 复制和粘贴多行代码。
这种做法真的很糟糕吗?有不同的方法吗?
谢谢!
我有 A 和 B 类。现在我需要编写一个新的 C 类,它将使用 A 和 B 中的一些字段和方法,但不是全部。(我将使用 A 和 B 中大约 50% 的东西)。
现在我正在考虑从 A 和 B 继承。但这会使 C 包含很多没有意义的字段和方法。
基本上我只是为了代码重用的目的而使用继承,否则我将不得不从 A 和 B 复制和粘贴多行代码。
这种做法真的很糟糕吗?有不同的方法吗?
谢谢!
继承没有“是一半”的概念,所以绝对不是这里要走的路。听起来这是作曲的主要案例。
这听起来像A
并且B
具有多个“功能”,因为它们中的一半足以组成一个C
.
我不确定这是否适用于您的情况,但请考虑将 A 分成两部分,A1
并具有inA2
所需的功能。然后对into和执行相同的操作。C
A1
B
B1
B2
A
然后将是A1
and的组合A2
,B
将是 and 的组合,并且B1
将是 and 的B2
组合。它们都没有任何不必要的功能或数据。C
A1
B1
正如 Herb Sutter 和 Andrei Alexandrescu 在他们的《C++ 编码标准》(第 34 项)一书中所解释的,继承是 C++ 允许您在两个类之间使用的最紧密的关系之一——它仅次于friend
类之间的关系。
根据OO 的 SOLID 原则,成功的设计应该以类之间的松散耦合为目标——这意味着将依赖关系保持在最低限度;这反过来意味着继承是一个应该小心使用的工具。
当然,依赖项通常需要存在于某个地方interface
,因此一个合理的折衷方案可以是从根本没有实现的类继承(类似于C# 和 Java 等语言中的所谓s)。例如
class IDriveable
{
public:
virtual void GoForward() = 0;
virtual void GoBackward() = 0;
};
class Car : public IDriveable { /* etc. */ };
class Bus : public IDriveable { /* etc. */ };
class Train : public IDriveable { /* etc. */ };
使用这种方法,如果您有在多个Drivable类之间重用的代码元素,您通常会使用组合或其他一些较弱的关系来消除重复的代码。
例如,也许您想将代码重TurnLeft
用于 aBus
和 aCar
但不是 a Train
where 左转是不合逻辑的,因此TurnLeft
最终可能会出现在一个单独的类中,它是Bus
and的成员Car
。
最终结果可能是少量的额外代码用于组合,但通常是不太复杂的设计,通常是更易于管理的设计。设计这样的代码没有任何硬性规则,因为它完全取决于您要解决的独特问题。
还有其他方法可以在不紧密耦合的情况下重用代码 - 模板允许您隐式定义接口,而无需包含纯virtual
函数的空类(模板提供额外的类型安全 - 这是一件非常好的事情,但它们在语法上有点复杂);
并且您可以使用一些方法std::function
和 lambdas 以便以更具功能性的风格重用代码 - 同样,在传递函数对象时通常不涉及紧密的依赖关系。
继承应该使用“is a”范式。
这意味着如果 C 是 A 和 B 的一种,则 C 应该从 A,B 继承。
如果您已经在使用多重继承,您可能希望将 A 和 B 分解为多个类,每个类处理一小段代码,然后只从你需要的东西继承 C - 这样 C 只会占用它需要的空间(假设 A 和 B 有很多成员)。
请记住,您的类的大小不受成员方法的影响,只有成员数据。
这是坏的。只有在逻辑上有意义时才使用继承。
改用组合(private
继承是组合的一种形式,但最好是老派):
class C
{
A a;
B b;
}
如果C
不是由A
and组成B
,您也可以改为保留指针,因此 的大小C
不会增加。
听起来您可以从重构代码中受益:
而不是这个:
class A
{
virtual void foo();
virtual void bar();
};
class B
{
virtual void biz();
virtual void baz();
};
class C : public A, public B
{
virtual void foo() { /* my stuff */ }
virtual void biz() { /* my other stuff */ +
// but I don't need bar or baz!
};
考虑在 C 中拆分您想要的 A 和 B 的概念上不同的部分:
class core_A
{
virtual void foo();
};
class A : public core_A
{
virtual void bar();
};
class core_B
{
virtual void biz();
};
class B : public core_B
{
virtual void baz();
};
class C : public core_A, public core_B
{
virtual void foo() { /* my stuff */ }
virtual void biz() { /* my other stuff */ +
// Great, I don't have bar or baz!
};