class Legs {
public:
void WalkAround() {... code for walking around goes here...}
};
class Arms {
public:
void GrabThings() {... code for grabbing things goes here...}
};
class InheritanceRobot : public Legs, public Arms {
public:
// WalkAround() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot {
public:
void WalkAround() {legs.WalkAround();}
void GrabThings() {arms.GrabThings();}
private:
Legs legs;
Arms arms;
};
class Walker // interface
{
public:
virtual void Walk() = 0;
}
class Legs : public Walker
{
public:
void Walk() {... code for walking around goes here...}
}
class Grabber // Interface
{
public:
virtual void GrabThings() = 0;
}
class Arms : public Grabber
{
public:
void GrabThings() {... code for grabbing things goes here...}
}
class InheritanceRobot : public Legs, public Arms
{
public:
// Walk() and GrabThings() methods are implicitly
// defined for this class since it inherited those
// methods from its two superclasses
};
class CompositionRobot
{
public:
CompositionRobot(Walker& walker, Grabber& grabber)
: legs(walker),
arms(grabber)
{}
void Walk() {legs.Walk();}
void GrabThings() {arms.GrabThings();}
private:
Walker& legs;
Grabber& arms;
};
2条答案
按热度按时间lskq00tm1#
当然可以,为什么不呢?既然我喜欢机器人,那我们就做一个能到处走、能抓东西的机器人吧。我们用继承来做一个机器人,用组合来做另一个机器人:
注意,至少对于这个例子,
CompositionRobot
通常被认为是更好的方法,因为继承意味着is-a
关系,并且机器人不是特定种类的Arms
,也不是特定种类的Legs
(而是机器人has-arms
和has-legs
)。68de4m5k2#
进一步扩展@jeremy-friesner的答案(并且主要是重用他的代码),很多时候组合是使用更多的类来实现的。本质上,Legs和Arms类是接口的实现。这使得很容易注入这些依赖项,因此,在单元测试组合对象时,可以模拟/清除它们。然后,你会得到类似的结果(忽略虚析构函数...):
因此,用于腿部和手臂的实际实现可以在运行时而不是编译时设置。
顺便说一句,我只是把这作为一个答案,而不是对杰里米的答案的评论,以受益于代码格式,所以,如果你想投票,请做杰里米的了。
高温加热
2021年9月14日更新:
在这个答案中我注意到的一件事是,我把组合和聚合混为一谈了。在组合中,如果父对象不再存在,那么子对象也不再存在,而在聚合中,父对象被销毁后子对象可能还存在。我给出的描述是,其中在CompositionRobot构造函数中传递对子对象示例的引用意味着聚合关系而不是组合。然而,如果在定义参数和创建对象时使用std::unique_ptr(),而在将对象存储在CompositionRobot的构造函数中时使用std::move(),则效果与Jeremy的答案中的效果大致相同,在Jeremy的答案中,对象(而不是指向对象的指针或引用)被定义为类成员。