我想为所有类型的纸牌游戏实施一个库。我发现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.
但我对设计非常不确定,因为它需要很多很多接口。
有没有更好,更正确的方法呢?因为库应该独立于编程语言,所以我不希望具体实现。
答案 0 :(得分:0)
这当然不是一个可以接受的答案,甚至可能是主观的,但......评论太长了,也许它鼓励其他人给出更深刻的答案:
只要描述在编程语言和预期用途方面如此模糊,就很难给出设计提示。例如。我不完全确定你是否将Card
称为几个CardCollection
中的一个 - 直觉上,单个卡片不是卡片的集合。另外,在你的描述中,你谈到了CardCollections
我只期望Card
的地方(例如“ ...索引转移后的所有CardCollections ...... ” - 不应该这是“ ...索引转移后所有卡 ... ”?)
但是,我认为关于精确继承关系的决定主要取决于这些类将包含的方法,以及它们的使用方式。鉴于信息有限,我的“通用”方法大致如下:
在相应的类中插入您期望的所有方法。第一个猜测:
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 {
???
}
排除常见部分。这可能很棘手。它取决于所选语言是否支持多继承或Java中的interface
等。更重要的是,它取决于语言是否支持非公共继承。例如,当您在Java中编写class ExtendedClass extends BaseClass
或class ExtendedClass implements BaseInterface
时,这就是一成不变的。使用API的每个人都将依赖此继承,并且在第一个版本发布后永远不会更改。与C ++相反,您可以在其中编写class ExtendedClass : private BaseClass
,并且API的用户永远不会知道您从BaseClass
继承。 (我不熟悉Tcl,Smalltalk或Lua,所以我不能对此说些什么 - 当然,C ++是不那么 - 易于学习的最好例子语言 ;-))。
根据公共部分构造继承层次结构。例如 可以根据您已经描述的内容,例如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 {}
当然,这再一次取决于语言和预期用途。人们还必须要问这些类是否应该作为“外观”,实际行为应该通过组合而不是继承来实现。另一个问题的一个明显例子是:每个期望Pile
的方法是否也应该接受Row
?也可以考虑类设计的替代方案。例如,不要让Row
继承FrontQueue
和EndQueue
,而是可以声明
Row {
FrontQueue getFront();
EndQueue getEnd();
}
同样,这些只是想法,在最坏的情况下,没有回答你的问题,而是提出新的问题,但是......也许是你必须在去往目标的路上回答的问题。
答案 1 :(得分:0)
您的设计已经完成了几次。除了卡之外,所有接口都非常类似于集合,列表,队列,堆栈和双端队列。有java collections framework,c# collections framework和dart。你可能会想出类似的东西。
我建议使用具体实现的尖峰,其中所有内容都扩展了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();
}