创建一个新对象 class 或编写一个转换子 class 对象的方法?或者是其他东西?性能不是偏好
Create a new object class or write a method which converts sub-class objects? or something else? Performance NOT Preference
我将两个单独的程序放在一起,它们可以玩一种名为 'Crazy Eights' 的纸牌游戏。
我为这个程序编写的 classes 基于默认的 'card' 包,它提供纸牌对象和一些通用的纸牌方法。
我采用了两种不同的方法来实现这一点,它们都具有各自的功能。
这里有两个描述这两种方法的 UML class 图:
继承子class'conversion'方法
用类似的方法组合子class
正如您在方法 1 中看到的那样,class EightsCard 包含一个方法 convert(Card) 这是该方法:
/**
* Converts a Card into an EightsCard
* @param card The card to be converted
* @return The converted EightsCard
*/
public EightsCard convert(Card card) {
if (card != null) {
EightsCard result = new EightsCard(card.getRank(), card.getSuit());
return result;
}
return null;
}
}
此方法允许您调用 CardCollection 中的方法,否则这些方法是不合法的。例如,在如下所示的 EightsPlayer class 的播放方法中:
/**
* Removes and returns a legal card from the player's hand.
*/
public EightsCard play(Eights eights, EightsCard prev) {
EightsCard ecard = new EightsCard(0, 0);
ecard = ecard.convert(searchForMatch(prev));
if (ecard == null) {
ecard = drawForMatch(eights, prev);
return ecard;
}
return ecard;
}
方法 2 不需要任何转换,因为类似的方法已写入扩展 CardCollection 的新 class EightsCardCollection。现在播放方法可以这样写:
public EightsCard play(Eights eights, EightsCard prev) {
EightsCard card = searchForMatch(prev);
if (card == null) {
card = drawForMatch(eights, prev);
}
return card;
}
这让我想到了几个问题:
- 除了个人偏好之外,这两种方法都有什么优势吗?
- 有没有更好的方法来编写这个程序?
例如,写 'similar' classes 更具体 1 而不使用默认的 classes 可能更好2 完全没有。
1 在 class 图表中标记为 'crazyeights.syd.jjj' 或 'chaptwelvetofort'。
2 在 class 图中标记为 'defaults.syd.jjj' 或 cards.syd.jjj'。
子太多classes
这两种方法都不是很好,因为它们都有比需要更多的子classes。为什么 EightsCard
扩展 Card
?如果您想实现其他纸牌游戏怎么办? (有很多,你知道的......)你会为每个纸牌游戏做一个子class吗?请不要。
我想要这些 classes:
- 卡片
- 卡片收藏
- 玩家
我什至不会有一个 Deck
class 因为它看起来除了扩展 CardCollection
没有提供任何更多的功能.
然后我会为每个游戏实现一个 class,所以一个 EightsController
class 处理该游戏的游戏逻辑。没有特定的 class 用于 EightsCard,没有特定的 class 用于 EightsCardCollection,等等
原因很简单:您只需要这个。无论您玩的是哪种纸牌游戏,Card 和 CardCollection 都是完全相同的。玩家在所有游戏中也是一样的。
关于那些域模型,我同意 Simon Forsberg 的观点,它们太复杂了,而且我知道它们旨在展示一些概念,但即便如此,它们也可以使用更现实的示例,例如组件在汽车和他们的关系。您可能只需要两个域 classes,Card 和 Player。对于有序的卡片集合,请使用 List<Card>
。在发牌、轮流等方面,如何组织游戏逻辑有很大的创意空间。 Generally it is preferable to compose types out of other ones 而不是严重依赖在实践中不太灵活的继承。
在性能方面,通用规则适用。例如,尽可能重用对象。如果您总是使用 52 张牌,那么甚至没有理由使用 Card class。可能没有什么比在任何地方重用枚举值更好的了。
enum Card {
ACE_CLUBS,
TWO_CLUBS,
// ...
ACE_DIAMONDS,
// ...
}
(你真的不能让这个枚举变得更复杂,因为不同的游戏使用不同的顺序,根据上下文在不同的卡片上放置不同的值,等等)
关于性能编程的优秀实用指南,我推荐这本书,Java Performance: The Definitive Guide。
西蒙的评论引起了我的注意:"I would prefer having two enums - one for Suit and one for Rank - instead of one enum for the whole card"
Is there a better way to compose this program?
关键点在于——您的大部分程序应该与卡片的内存表示完全隔离。
在内存中,一张卡可能是
- 一个整数
- 一个字节
- 枚举
- 一个字符串
- 一个URL (http://example.org/cards/hearts/7)
- an integer/byte 限制在 [0,52)
范围内
- 一个元组
- 整数,整数
- 枚举,枚举
- 字节,字节
- 混搭
- 对第三方实施的引用
你的大部分代码不应该关心。
理想情况下,唯一关心的是提供实现的模块,也许还有组合根。
图表中大多数有 <<Java Class>>
的地方你应该有 <<Java Interface>>
.
在那种设计中,您将使用 组合 将疯狂的八语义附加到其他地方定义的通用卡片定义。
Eights.Deck deck(Iterable<Generic.Card> standardDeck) {
List<Eights.Card> cards = new ArrayList();
for(Generic.Card c : standardDeck) {
cards.add(
new Eights.Card(c)
);
}
return new Eights.Deck(cards);
}
或者也许
Eights.Deck deck(Generic.Deck standardDeck) {
return new Eights.Deck(
standardDeck
.stream()
.map(Eights.Deck::new)
.collect(Collectors.toList())
);
}
组合根看起来像
public static void main(String [] args) {
GenericCards genericCards = new ApacheGenericCards();
EightsCards eightsCards = MyAwesomEightsCardsV2(genericCards);
EightsGame game = new EightsGame(eightsCards)
game.play();
}
我将两个单独的程序放在一起,它们可以玩一种名为 'Crazy Eights' 的纸牌游戏。
我为这个程序编写的 classes 基于默认的 'card' 包,它提供纸牌对象和一些通用的纸牌方法。
我采用了两种不同的方法来实现这一点,它们都具有各自的功能。
这里有两个描述这两种方法的 UML class 图:
继承子class'conversion'方法
用类似的方法组合子class
正如您在方法 1 中看到的那样,class EightsCard 包含一个方法 convert(Card) 这是该方法:
/**
* Converts a Card into an EightsCard
* @param card The card to be converted
* @return The converted EightsCard
*/
public EightsCard convert(Card card) {
if (card != null) {
EightsCard result = new EightsCard(card.getRank(), card.getSuit());
return result;
}
return null;
}
}
此方法允许您调用 CardCollection 中的方法,否则这些方法是不合法的。例如,在如下所示的 EightsPlayer class 的播放方法中:
/**
* Removes and returns a legal card from the player's hand.
*/
public EightsCard play(Eights eights, EightsCard prev) {
EightsCard ecard = new EightsCard(0, 0);
ecard = ecard.convert(searchForMatch(prev));
if (ecard == null) {
ecard = drawForMatch(eights, prev);
return ecard;
}
return ecard;
}
方法 2 不需要任何转换,因为类似的方法已写入扩展 CardCollection 的新 class EightsCardCollection。现在播放方法可以这样写:
public EightsCard play(Eights eights, EightsCard prev) {
EightsCard card = searchForMatch(prev);
if (card == null) {
card = drawForMatch(eights, prev);
}
return card;
}
这让我想到了几个问题:
- 除了个人偏好之外,这两种方法都有什么优势吗?
- 有没有更好的方法来编写这个程序?
例如,写 'similar' classes 更具体 1 而不使用默认的 classes 可能更好2 完全没有。
1 在 class 图表中标记为 'crazyeights.syd.jjj' 或 'chaptwelvetofort'。
2 在 class 图中标记为 'defaults.syd.jjj' 或 cards.syd.jjj'。
子太多classes
这两种方法都不是很好,因为它们都有比需要更多的子classes。为什么 EightsCard
扩展 Card
?如果您想实现其他纸牌游戏怎么办? (有很多,你知道的......)你会为每个纸牌游戏做一个子class吗?请不要。
我想要这些 classes:
- 卡片
- 卡片收藏
- 玩家
我什至不会有一个 Deck
class 因为它看起来除了扩展 CardCollection
没有提供任何更多的功能.
然后我会为每个游戏实现一个 class,所以一个 EightsController
class 处理该游戏的游戏逻辑。没有特定的 class 用于 EightsCard,没有特定的 class 用于 EightsCardCollection,等等
原因很简单:您只需要这个。无论您玩的是哪种纸牌游戏,Card 和 CardCollection 都是完全相同的。玩家在所有游戏中也是一样的。
关于那些域模型,我同意 Simon Forsberg 的观点,它们太复杂了,而且我知道它们旨在展示一些概念,但即便如此,它们也可以使用更现实的示例,例如组件在汽车和他们的关系。您可能只需要两个域 classes,Card 和 Player。对于有序的卡片集合,请使用 List<Card>
。在发牌、轮流等方面,如何组织游戏逻辑有很大的创意空间。 Generally it is preferable to compose types out of other ones 而不是严重依赖在实践中不太灵活的继承。
在性能方面,通用规则适用。例如,尽可能重用对象。如果您总是使用 52 张牌,那么甚至没有理由使用 Card class。可能没有什么比在任何地方重用枚举值更好的了。
enum Card {
ACE_CLUBS,
TWO_CLUBS,
// ...
ACE_DIAMONDS,
// ...
}
(你真的不能让这个枚举变得更复杂,因为不同的游戏使用不同的顺序,根据上下文在不同的卡片上放置不同的值,等等)
关于性能编程的优秀实用指南,我推荐这本书,Java Performance: The Definitive Guide。
西蒙的评论引起了我的注意:"I would prefer having two enums - one for Suit and one for Rank - instead of one enum for the whole card"
Is there a better way to compose this program?
关键点在于——您的大部分程序应该与卡片的内存表示完全隔离。
在内存中,一张卡可能是
- 一个整数
- 一个字节
- 枚举
- 一个字符串
- 一个URL (http://example.org/cards/hearts/7)
- an integer/byte 限制在 [0,52) 范围内
- 一个元组
- 整数,整数
- 枚举,枚举
- 字节,字节
- 混搭
- 对第三方实施的引用
你的大部分代码不应该关心。
理想情况下,唯一关心的是提供实现的模块,也许还有组合根。
图表中大多数有 <<Java Class>>
的地方你应该有 <<Java Interface>>
.
在那种设计中,您将使用 组合 将疯狂的八语义附加到其他地方定义的通用卡片定义。
Eights.Deck deck(Iterable<Generic.Card> standardDeck) {
List<Eights.Card> cards = new ArrayList();
for(Generic.Card c : standardDeck) {
cards.add(
new Eights.Card(c)
);
}
return new Eights.Deck(cards);
}
或者也许
Eights.Deck deck(Generic.Deck standardDeck) {
return new Eights.Deck(
standardDeck
.stream()
.map(Eights.Deck::new)
.collect(Collectors.toList())
);
}
组合根看起来像
public static void main(String [] args) {
GenericCards genericCards = new ApacheGenericCards();
EightsCards eightsCards = MyAwesomEightsCardsV2(genericCards);
EightsGame game = new EightsGame(eightsCards)
game.play();
}