使用界面
它为学生提供了一种在他们编写的代码中使用卡片的简单方法,而无需访问
卡片的内部结构,无法创建特定的卡片,也不知道卡片的原理
实施的。这个过程从卡接口的代码开始,我们称之为 ICard 的接口。
public interface ICard extends Comparable
{
public static final int SPADES = 0;
public static final int HEARTS = 1;
public static final int DIAMONDS = 2;
public static final int CLUBS = 3;
public int getSuit();
public int getRank();
}
接口指定卡片的行为,但不提供卡片如何使用的信息
实施的。一旦他们知道getSuit() 返回一个类似 ICard.HEARTS 的值,并且
getRank()返回一个1(ace)到13(king)范围内的值,同学们可以从这里写代码
规格。例如,这是检查卡片数组是否已排序的代码。我们不
知道它是如何排序的(例如,是所有的 A 都在两个之前,还是所有的黑桃都来
在红心之前?),但我们可以确定一个数组是排序的。
1 以大写 I 开头的接口名称,后跟大写的名称,是一种常见的命名方式
许多语言的面向对象编程中的约定,而不仅仅是 Java。
public boolean isSorted(ICard[] list){
for(int k=1; k < list.length; k++){
if (list[k-1].compareTo(list[k]) > 0){
return false;
}
}
return true;
}
从这个简单的ICard界面开始,我们可以问学生多种
用于测试和审查从 Java 语法到问题解决的概念的问题
关于一张、两张或多张牌。这里包括一些简单的例子,
网站上提供了更多信息。在回答这些问题时,学生必须
了解接口,因为没有实现。学生专注于
行为而不是实例变量和其他实现细节,例如
如何创建一个字符串来表示黑桃 A。
ICard 学习/代码问题
编写函数 isRed,如果其 ICard 参数为红色(红心或
钻石),否则返回 false。
public boolean isRed(ICard card){…}
对子是两张相同点数的牌(例如,两张 K 或两张 8)。编写函数
isPair 如果其两个 ICard 参数表示一对,则返回 true 并返回 false
否则。
public boolean isPair(ICard a, ICard b){…}
同花是一手牌,比如扑克牌,其中所有的牌都有相同的花色(例如,五张红心,
或五张梅花五张牌)。编写函数 isFlush,如果
卡片数组是同花顺,否则返回 false。
public boolean isFlush(ICard[] hand){…}
-
在 21 点或 21 点中,一手牌的价值是牌的总和,其中 J、Q 和
国王(分别为 11、12 和 13,由 getRank() 返回)每个都算作 10,并且
ace 算作 1 或 10,以更好的为准。总共超过 21 是半身像;破产不好。
编写函数handTotal,它返回一手牌的总值。
public int handTotal(ICard[] hand){…}
从接口到实现
ICard 接口提供了足够的信息来编写关于卡片的代码,
但是没有办法创建一个卡片数组,例如,甚至一个
card 来测试上面写的函数(比如 isPair 和 handTotal)。在哪里
卡是从哪里来的?在大多数现实世界的例子中,卡片来自甲板。好吧
设计一个模拟 Deck 的类——它基本上是一个创建和获取卡片的工厂。
为了简单起见,并鼓励学习一些标准的 Java 接口,该类
Deck 将实现 java.util.Iterator 接口。例如,要存储所有
将卡片中的卡片放入 ArrayList 变量中,我们可以使用以下代码:
甲板 d = 新甲板();
ArrayList 卡片 = 新的 ArrayList();
而(d.hasNext()){
ICard 卡 = (ICard) d.next();
System.out.println(card);
卡片。添加(卡片);
}
System.out.println("发牌数 = " + cards.size());
这段代码 sn-p 输出的最后几行可能如下所示。他们会有所不同
每次都是因为这里开发的 Deck 类通过迭代洗牌它处理的牌。
…
黑桃王牌
千斤顶
黑桃六
红心十
黑桃十
发牌数 = 52
如果我们如下改变循环后的行,输出也会改变。
Collections.sort(cards);
for(int k=0; k < cards.size(); k++){
System.out.println(cards.get(k));
}
System.out.println("发牌数 = " + cards.size());
输出显示了从 Deck 类返回的卡片如何实现 Comparable 接口。
…
九个俱乐部
十家具乐部
千斤顶
夜店女王
俱乐部之王
发牌数量 = 52
Deck 类的完整代码如下所示。方法 hasNext()、next() 和
实现 Iterator 接口的类需要 remove()。下面的代码
显示了 Card 类型的对象是如何构造的。
public class Deck implements Iterator{
private ArrayList myCardList;
private int myIndex;
public Deck(){
myCardList = new ArrayList();
for(int suit = ICard.SPADES; suit <= ICard.CLUBS; suit++){
for (int rank = 1; rank <= 13; rank++){
myCardList.add(new Card(suit,rank));
}
}
shuffle();
}
private void shuffle(){
Collections.shuffle(myCardList);
myIndex = 0;
}
public boolean hasNext() {
return myIndex < myCardList.size();
}
public Object next() {
ICard card = (ICard) myCardList.get(myIndex);
myIndex++;
return card;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
一个 Deck 对象存储 52 张卡片 --- 这些卡片可以从一个 Deck 中获得
对象通过迭代,但 Deck 对象不能重新洗牌和重用。
相反,必须创建一个新的 Deck 对象来发新牌。这保持
事情很简单,并提供了一个易于理解的类示例
实现迭代器接口。方法 remove() 是可选的 --- 对于
调用此方法的 Deck 类会引发异常。
套牌研究/代码问题
就在构造函数中调用shuffle方法之前,描述
存储在 myCardList 中的对象。
描述当实例变量 myCardList 改变时每个 Deck 方法如何改变
到 Card 对象的数组,例如,
私人 ICard[] myCardList;
myCardList 的哪个选择更好?为什么?
编写客户端代码,定义一个 Deck 对象并创建一个包含 13 个 ICard 对象的数组
代表从甲板上处理的黑桃。通过检查处理的每个对象来做到这一点
并且只存放黑桃牌。
-
编写下面指定的假设 Hand 类构造函数的主体
私有 ArrayList myCards;
/**
- 从 d 处理 numCards 卡,存储在 myCards 中
- (假设 d 中至少还剩 numCards 卡)
*/
公共手(Deck d,int numCards){
}
从套牌到卡片
我们最初关心的是使用 ICard 接口,而不是担心如何
卡实施。然而,在某些时候,需要有一个
执行。不难争论 Card 对象应该由
甲板课。这是我们在这里使用的方法。 Card 类是一个私有类
在 Deck 类中声明。实际上没有充分的理由在内部声明它
甲板(Deck.java 文件)。但是,通过将其声明为私有,我们将其设为
任何代码 class2 都不可能;它可以很容易地声明为内部的非公共类
Deck 类以外的方法来构造 Card 对象。这有助于实现我们最初的目标。
客户端程序可以从 Deck 中获取卡片,但不能创建卡片。由于甲板供应
ICard 对象,一旦从 Deck 中获得卡片,就无法更改卡片,因为
ICard 接口不支持修改卡片。
正如所写,在 Deck 类中定义的私有 Card 类不支持
修改,因为它的私有状态实例变量是最终的,但这是额外的
可能不需要的保护,因为没有客户端代码可以访问私有 Card 类。
2 通常在另一个类中声明的类经常引用封闭对象的状态。在这个
如果嵌套类 Card 被声明为私有静态类,因此它不能引用私有非静态状态
在 Deck 对象中。 Card 类可以引用静态 Deck 状态,但此代码中没有。
Card 类在 Web 站点上可用;自实施以来,我们没有将其包括在此处
与我们关于设计的讨论没有直接关系。
细心的读者可能会声称我们最初的目标没有实现。客户端代码可以作弊,因为
例如,通过创建一个 Deck 对象,然后从该对象发牌直到一张 A 空格
(或任何其他牌)被处理。在 Deck 类的当前设计中,这是正确的。然而,我们
可以创建一个单独的 Deck 对象,就像类 Random 的单个实例一样
用于海洋生物学案例研究。单例对象通常通过声明创建
构造函数,以便它们是私有的。在这种情况下,Deck 构造函数将从 public 更改为
私人的。客户端代码通过调用公共的 getInstance() 方法获取 Deck,该方法
返回存储在 Deck 类中的私有静态 Deck 对象。 getInstance 方法
在第一次调用 getInstance 时创建此私有对象。