簡體   English   中英

創建一個新的對象類或編寫一個轉換子類對象的方法?或者是其他東西?表現不是偏好

[英]Create a new object class or write a method which converts sub-class objects? or something else? Performance NOT Preference

我把兩個單獨的程序放在一起,玩一個名為'Crazy Eights'的紙牌游戲。

我為這個程序編寫的類基於一個默認的“card”包,它提供了撲克牌對象和撲克牌的一些通用方法。

我采取了兩種不同的方法來實現這一點,這些方法本身都是功能性的。

這是兩個UML類圖,描述了兩種方法:

繼承子類'轉換'方法

在此輸入圖像描述

具有類似方法的組合子類

在此輸入圖像描述

正如你在方法1中看到的,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類的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不需要任何轉換,因為類似的方法已經在擴展CardCollection的新類EightsCardCollection中編寫。 現在播放方法可以這樣寫:

  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'。

子類太多了

這兩種方法都不是很好,因為它們都有不必要的子類。 為什么EightsCard擴展Card 如果你想實施其他紙牌游戲怎么辦? (有很多,你知道......)你會為每個紙牌游戲制作一個子類嗎? 請不要。

我會有這些課程:

  • CardCollection
  • 播放機

我甚至沒有Deck類,因為它看起來除了擴展CardCollection 而沒有提供更多功能之外別無其他。

然后我會為每個游戲實現一個類,所以一個EightsController類處理該游戲的游戲邏輯。 沒有針對EightsCard的特定類,沒有EightsCardCollection的特定類等。

原因很簡單:除此之外你不需要任何東西。 無論您正在玩哪種紙牌游戲,卡和CardCollection都完全相同。 所有游戲中的玩家也是同樣的東西。

關於那些領域模型,我同意Simon Forsberg的說法太復雜了,我知道它們的目的是為了展示一些概念,但即便如此,他們也可以使用更現實的例子,比如汽車中的組件和他們的關系。 您可能只需要兩個域類,即Card和Player。 對於有序的卡片集合,請使用List<Card> 在談到交易,輪流等方面,如何組織游戲邏輯有很大的創造空間。 通常,最好是從其他類型中組合類型,而不是在很大程度上依賴於在實踐中不太靈活的繼承。

在性能方面,適用通用規則。 例如,盡可能重用對象。 如果你總是使用52張牌,那么就沒有理由讓你上牌。 可能沒有比在任何地方重用枚舉值更好的表現了。

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

(你不能真正使這個枚舉更復雜,因為不同的游戲使用不同的順序,根據上下文在不同的卡上放置不同的值等)

對於性能編程的優秀實用指南,我推薦“ Java性能:權威指南”一書

西蒙的評論引起了我的注意:“我寧願有兩個枚舉 - 一個用於套裝,一個用於排名 - 而不是整張卡的一個枚舉”

有沒有更好的方法來編寫這個程序?

關鍵在於 - 您的大多數程序應該與卡的內存表示完全隔離。

在內存中,卡可能是

  • 整數
  • 一個字節
  • 一個枚舉
  • 一個字符串
  • 一個URL( http://example.org/cards/hearts/7
  • 限制在[0,52]范圍內的整數/字節
  • 一個元組
    • int,int
    • enum,enum
    • 字節,字節
    • 連連看
  • 對第三方實施的引用

你的大部分代碼都不應該關心

理想情況下,唯一關心的位是提供實現的模塊,也許是組合根。

你的圖中大多數你擁有<<Java Class>>都應該有<<Java Interface>>

Parnas,1972年

在那種設計中,您將使用組合將您瘋狂的八個語義附加到其他地方定義的卡的通用定義。

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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM