通用纸牌游戏库中的馆藏设计

时间:2014-02-01 12:32:52

标签: oop collections

我想为所有类型的纸牌游戏实施一个库。我发现OOP可能是实现这样一个库的最佳方式。所以我开始为UI的一些类(具有具体的子类ConsoleUI,SDLUI,HTMLUI),Party(具有子类Player和Group),Card,CardAttribute,Turn,TurnCommand,CompoundTurnCommand建模。那些课程和完整一样好。

但现在我发现有几种不同类型的卡片。我想创建一个带有许多子类的抽象类CardCollections:

CardCollection = abstract collection of CardCollections
Card = a single card
Hand = an indexed random access list. If you execute draw(index), all CardCollections after index are shifted left (like a linked list)
Tablet = an indexed random access list. If you execute draw(index), the CardCollections after index are not shifted, can contain gaps between cards (like an array/dictionary)
Pile = an LIFO collection (like a stack)
Stock = a FIFO collection (like a queue)
Row = a double-ended collection (like a deque)
Random = a non-indexed undordered collection (like a bag)

因为我想遵循常见的OOP设计实践,比如继承和多态,我正在寻找一个合适的模型 - 这就是问题的开始。

我开始为所有具体类创建接口:

ICard extends IHand and ITablet.
IHand extends IRandom and IRow.
ITablet extends ICardCollection.
IPile extends ICardCollection.
IStock extends ICardCollection.
IRow extends IPile and IStock.
IRandom extends ICardCollection.

但我对设计非常不确定,因为它需要很多很多接口。

有没有更好,更正确的方法呢?因为库应该独立于编程语言,所以我不希望具体实现。

2 个答案:

答案 0 :(得分:0)

这当然不是一个可以接受的答案,甚至可能是主观的,但......评论太长了,也许它鼓励其他人给出更深刻的答案:

只要描述在编程语言和预期用途方面如此模糊,就很难给出设计提示。例如。我不完全确定你是否将Card称为几个CardCollection中的一个 - 直觉上,单个卡片不是卡片的集合。另外,在你的描述中,你谈到了CardCollections我只期望Card的地方(例如“ ...索引转移后的所有CardCollections ...... ” - 不应该这是“ ...索引转移后所有 ... ”?)

但是,我认为关于精确继承关系的决定主要取决于这些类将包含的方法,以及它们的使用方式。鉴于信息有限,我的“通用”方法大致如下:

  1. 在相应的类中插入您期望的所有方法。第一个猜测:

    CardCollection  {
        int size(); 
    }
    
    Hand {
        Card draw(int index);
    }
    
    Tablet {
        Card draw(int index); 
    }
    
    Pile {
        void putFront(Card card);
        Card takeFront();
    }
    
    Stock {
        void putEnd(Card card);
        Card takeEnd();
    }
    
    Row {
        void putFront(Card card);
        Card takeFront();
        void putEnd(Card card);
        Card takeEnd();
    }
    
    Random {
        ???
    }
    
  2. 排除常见部分。这可能很棘手。它取决于所选语言是否支持多继承或Java中的interface等。更重要的是,它取决于语言是否支持非公共继承。例如,当您在Java中编写class ExtendedClass extends BaseClassclass ExtendedClass implements BaseInterface时,这就是一成不变的。使用API​​的每个人都将依赖此继承,并且在第一个版本发布后永远不会更改。与C ++相反,您可以在其中编写class ExtendedClass : private BaseClass,并且API的用户永远不会知道您从BaseClass继承。 (我不熟悉Tcl,Smalltalk或Lua,所以我不能对此说些什么 - 当然,C ++是不那么 - 易于学习的最好例子语言 ;-))。

  3. 根据公共部分构造继承层次结构。例如 可以根据您已经描述的内容,例如Row extends Pile and Stock。或者,您可以根据它们允许的内容对类进行分类,可能采用某种抽象形式,甚至可能对用户不可见:

    // Private part:
    FrontQueue {
        void putFront(Card card);
        Card takeFront();
    }
    EndQueue {
        void putEnd(Card card);
        Card takeEnd();
    }
    
    // Public part:
    Pile extends FrontQueue {}
    Stock extends EndQueue {}
    Row extends FrontQueue, EndQueue {}
    
  4. 当然,这再一次取决于语言和预期用途。人们还必须要问这些类是否应该作为“外观”,实际行为应该通过组合而不是继承来实现。另一个问题的一个明显例子是:每个期望Pile的方法是否也应该接受Row?也可以考虑类设计的替代方案。例如,不要让Row继承FrontQueueEndQueue,而是可以声明

    Row {
        FrontQueue getFront();
        EndQueue getEnd();
    }
    

    同样,这些只是想法,在最坏的情况下,没有回答你的问题,而是提出新的问题,但是......也许是你必须在去往目标的路上回答的问题。

答案 1 :(得分:0)

您的设计已经完成了几次。除了卡之外,所有接口都非常类似于集合,列表,队列,堆栈和双端队列。有java collections frameworkc# collections frameworkdart。你可能会想出类似的东西。

我建议使用具体实现的尖峰,其中所有内容都扩展了collection,list,queue,stack或deque,然后重构出你可以找到的任何接口层次结构。

编辑:我刚注意到你有:ICard扩展了IHand和ITablet。卡可能是它自己的界面。类似的东西:

enum Type {poker, tarot, magic ... }
interface {
     Type type();
     Map<String,Object> attributeNameToAttributeValue();
}

你可以有卡片口味的子接口,如:

enum Suit { clubs, diamonds, hearts, spades }
enum Rank { deuce,trey,four,five,six,seven,eight,nine,ten,jack,queen,king,ace }
interface PokerCard extends Card {
     Rank rank();
     Suite suit();
}