Redundant code in composition class C++
我正在尝试拿起 C 。一切都很顺利,直到我的"练习"计划遇到了很小的障碍。我相信,这个障碍源于设计问题。
想想二十一点(21)。我做了几节课。
一副牌包括——为了简单起见——有一组卡片。
- 它可以显示所有的卡片
- 它可以洗牌
-它可以移除卡片
一手牌就是一副牌——它的好处是
- 它可以计算它的手值
-它可以添加卡片到手
现在开始我的问题 - 播放器设计
-玩家有手(私人访问)
我对播放器的问题是,那只手有一个名为 addCardToHand 的方法函数。如果我必须创建一个名为 addCardToHand(Card c) 的 Player 方法,在该方法中调用并传递给手头的同一方法,我会感到冗余/糟糕的设计。
或
将 Hand h 声明为可公开访问的成员,并在 'main()' 中执行类似的操作
玩家 p;
卡卡;
p.h.addCard(aCard);
任何建议都会很有启发性并受到高度赞赏。请记住我正在学习。
这里最好的答案是:这取决于:) 不过,我会尽量澄清一下。
第一个问题是:Player类有没有内部逻辑?如果是Hand的简单容器,我就直接写
现在让我们假设,需要实现额外的逻辑来将一张牌添加到玩家的手上。这意味着,在将牌添加到手牌时,必须调用 Player 类中的附加代码。在这种情况下,我看到了三种可能的解决方案。
(来源仅用于演示目的,可能无法编译)
-
限制对 Hand 的访问,这样任何人都无法从 Player 中检索它。播放器必须实现 AddToHand、RemoveFromHand 等方法。可行,但使用起来不舒服。
1
2
3
4
5
6
7
8
9
10
11class Player
{
private:
Hand hand;
public:
void AddToHand(Card & card)
{
hand.Add(card);
}
}; -
使用观察者模式。当用户(类用户)调用 Player.GetHand().AddCard() 时,Hand 会通知 Player,数据已更改,并且 Player 可以采取相应的行动。你可以很容易地使用 C 11 中的 std::function 来实现事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34class Deck
{
private:
std::function<void(void)> cardsChanged;
public:
void Add(Card card)
{
// Add a card
if (!(cardsChanged._Empty()))
cardsChanged();
}
void SetCardsChangedHandler(std::function<void(void)> newHandler)
{
cardsChanged = newHandler;
}
};
// (...)
class Player
{
private:
Hand hand;
void CardsChanged() { ... }
(...)
public:
Player()
{
hand.SetCardsChangedHandler([&this]() { this.CardsChanged(); } );
}
}; -
使用所有必要的接口方法定义 IHand 接口。 Hand 显然应该实现 IHand 并且 Player.GetHand() 应该返回 IHand。诀窍是,Player 返回的 IHand 不一定必须是 Hand 实例,而是可以是充当用户和真实 Hand 实例之间桥梁的装饰器(参见装饰器模式)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51class IHand
{
public:
virtual void Add(Card card) = 0;
virtual void Remove(Card card) = 0;
};
class Hand : public IHand
{
// Implementations
}
class PlayersHand : public IHand
{
private:
Hand & hand;
Player & player;
public:
PlayersHand(Hand & newHand, Player & newPlayer)
{
hand = newHand;
player = newPlayer;
}
void Add(Card card)
{
hand.Add(card);
player.HandChanged();
}
// ...
};
class Player
{
private:
Hand hand;
PlayersHand * playersHand;
public:
Player()
{
playersHand = new PlayersHand(hand, this);
}
IHand GetHand()
{
return playersHand;
}
}
就个人而言,在第二种情况下,我会选择第二种解决方案——它非常简单,易于扩展和重用,以防进一步需要。
功能呼叫转移是一种常见的做法。您应该将其视为添加某种程度的抽象。这不是再次做同样的事情(冗余意味着),而是使用另一种方法实现一种方法。
您可以想象将来会进行一些修改,例如添加
另请注意,