考虑两个类Box
和Rabbit
与一对一的简单关系 - 盒子最多可以包含一只兔子,兔子最多可以放在一个盒子里。
在伪代码中,我们有以下界面:
class Box
- getRabbit():Rabbit
- putRabbit(rabbit:Rabbit)
class Rabbit
- getContainingBox():Box
- putIntoBox(box:Box)
当然,我们希望保持不变:box.getRabbit().getContainingBox() == box
,如果box
不为空,则rabbit
相同。
在C ++中,我们可以将函数putRabbitIntoBox(Rabbit* rabbit, Box* box)
声明为Box
和Rabbit
的朋友,并根据{{1}实现Box::putRabbit
和Rabbit::putIntoBox
}}。而且我没有看到这种方法有任何重大缺点(如果我错了,请纠正我):
putRabbitIntoBox
现在我们决定用Java(或C#,或任何没有class Box;
class Rabbit;
void putRabbitIntoBox(Rabbit* rabbit, Box* box);
class Rabbit {
public:
Rabbit() :box(nullptr) {}
Box* getContainingBox() { return box; }
void putIntoBox(Box* box) { putRabbitIntoBox(this, box); }
private:
Box* box;
friend void putRabbitIntoBox(Rabbit* rabbit, Box* box);
};
class Box {
public:
Box() :rabbit(nullptr) {}
Rabbit* getRabbit() { return rabbit; }
void putRabbit(Rabbit* rabbit) { putRabbitIntoBox(rabbit, this); }
private:
Rabbit* rabbit;
friend void putRabbitIntoBox(Rabbit* rabbit, Box* box);
};
void putRabbitIntoBox(Rabbit* rabbit, Box* box) {
if (rabbit && rabbit->box) {
rabbit->box->rabbit = nullptr;
}
if (box && box->rabbit) {
box->rabbit->box = nullptr;
}
if (rabbit) {
rabbit->box = box;
}
if (box) {
box->rabbit = rabbit;
}
}
函数的语言)实现相同的Box
/ Rabbit
。
有解决这个问题的惯用方法吗?
我知道可以通过从friend
调用putRabbit
来解决这个问题,反之亦然,使用一些保护代码,如下所示:
putIntoBox
但这对我来说太可怕了。我们有一个非常容易出错的递归“事物”,而不是一个有明确定义目的的函数。
答案 0 :(得分:1)
您的约束是:
所以 - 只需在Box和Rabbit中实现这些约束。第一件事 - 忘记为一切定义setter和getter - 只需实现你需要的函数 - 仅此而已。
所以 - 让我们定义接口:
class Box;
class Rabbit {
public:
~Rabbit() { disconnect(); }
void connect(Box* box);
Box* getMyBox() const { return box; }
private:
void disconnect();
Box* box = nullptr;
};
class Box {
public:
~Box() { disconnect(); }
void connect(Rabbit* otherRabbit);
Rabbit* getMyRabbit() const { return rabbit; }
private:
void disconnect();
Rabbit* rabbit = nullptr;
};
正如您所看到的 - 通过在每个类中为其配合类定义一个指针,可以满足前两个约束。
Sp唯一的左边约束是确保两个对象(Rabbit和Box)相互连接 - 或者根本没有连接:
inline void Rabbit::connect(Box* otherBox)
{
if (box != otherBox)
{
disconnect();
box = otherBox;
if (box != nullptr)
box->connect(this);
}
}
inline void Rabbit::disconnect()
{
if (box != nullptr)
{
Box* oldBox = box;
box = nullptr; // do this before calling disconnect on box - otherwise you get infinite recursion...
oldBox->connect(nullptr);
}
}
您必须在Box
中定义类似的功能。关于ideone
在C ++中你可以使用模板 - 因为正如你所看到的 - 这两个类的定义方式非常相似......虽然我不能说模板是否会解决其他语言中的相同问题......
答案 1 :(得分:0)
你应该重新考虑你的课堂设计。在您的示例中,将Rabbit
放入Box
会更改两个类的状态。通常,可变状态是应尽可能避免的。在这种情况下,Box
改变其状态是可以接受的,但为什么Rabbit
也应该改变?
在两个类中都有相同的信息也会导致不一致。
因此,我只会定义:
class Box {
public:
putRabbit(Rabbit r);
Rabbit getRabbit();
...
}
Rabbit
类没有与Box
相关的任何方法。如果您将所有框存储在某个容器中,并且您实现了以下方法,那么您仍然可以找到Box
Rabbit
的内容:
Rabbit r;
...
Box b = allBoxes.findRabbitBox(r);