创建一个新的对象类或编写一个转换子类对象的方法?还是别的什么?性能而非偏好

2022-09-02 13:42:09

我把两个独立的程序放在一起,玩一个名为“Crazy Eights”的纸牌游戏。

我为此程序编写的类基于默认的“card”包,该包提供扑克牌对象和一些用于扑克牌的通用方法。

我采取了两种不同的方法来实现这一目标,这两种方法本身都是功能性的。

下面是两个描述这两种方法的 UML 类图:

继承的子类“转换”方法

enter image description here

使用类似方法组合的子类

enter image description here

正如您在方法1中看到的,Class EightsCard包含一个方法转换(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 类的 play 方法中,如下所示:

  /**
   * 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不需要任何转换,因为类似的方法已经写在一个新的类EightsCardCollection中,它扩展了CardCollection。现在播放方法可以这样写:

  public EightsCard play(Eights eights, EightsCard prev) {
    EightsCard card = searchForMatch(prev);
    if (card == null) {
      card = drawForMatch(eights, prev);
    }
    return card;
  }

这让我想到了几个问题:

  • 除了个人偏好之外,这两种方法有什么好处吗?
  • 有没有更好的方法来编写这个程序?

例如,编写更具体的“类似”类1 并且根本不使用默认类2 是否更好。

1 在类图中标记为“crazyeights.syd.jjj”或“chaptwelvetofort”。

2 在类图中标有“defaults.syd.jjj” 或 cards.syd.jjj'。


答案 1

子类太多

这两种方法都不是很好,因为它们都有比必要的更多的子类。为什么要扩展?如果你想实现其他一些纸牌游戏怎么办?(有很多,你知道的...)你会为每个纸牌游戏制作一个子类吗?请不要。EightsCardCard

我会有这些类:

  • 卡片集合
  • 选手

我甚至不会有一个类,因为它似乎除了扩展而不提供更多功能之外什么都不做。DeckCardCollection

然后,我将为每个游戏实现设置一个类,即一个处理该游戏的游戏逻辑的类。EightsCard没有特定的类,EightsCardCollection没有特定的类,等等。EightsController

原因很简单:你不需要更多的东西。一张牌和一张牌集合是完全相同的,无论你玩的是哪个纸牌游戏。玩家在所有游戏中也是一样的。


答案 2

关于这些领域模型,我同意西蒙·福斯伯格(Simon Forsberg)的观点,即这些模型太复杂了,我知道它们旨在演示一些概念,但即使这样,他们也可以使用一个更现实的例子,比如汽车中的组件及其关系。您可能只需要两个域类,卡牌和玩家。对于卡片的有序集合,请使用 .当涉及到交易,轮流等时,如何组织游戏逻辑有很大的创造力空间。通常,最好从其他类型中组合类型,而不是严重依赖在实践中不太灵活的继承。List<Card>

在性能方面,适用通用规则。例如,在可能的情况下重用对象。如果你总是使用52张牌的套牌,那么甚至没有理由有一个卡牌类。可能没有什么比在任何地方重用枚举值更好的了。

enum Card {
    ACE_CLUBS,
    TWO_CLUBS,
    // ...
    ACE_DIAMONDS,
    // ...
}

(你不能真的让这个枚举变得更加复杂,因为不同的游戏使用不同的顺序,根据上下文在不同的卡片上放置不同的值,等等。

对于性能编程的优秀实用指南,我推荐《Java性能:权威指南》一书。


推荐