3

我正在为家庭作业编写简单的纸牌游戏“战争”,现在游戏可以运行了,我正在尝试使其更加模块化和有条理。下面是Main()包含大部分程序的部分。我应该提一下,该课程是用 C# 教授的,但它不是 C# 课程。相反,我们正在学习基本逻辑和 OOP 概念,因此我可能不会利用某些 C# 功能。

bool sameCard = true;

while (sameCard)
{
    sameCard = false;
    card1.setVal(random.Next(1,14));        // set card value
    val1 = determineFace(card1.getVal());   // assign 'face' cards accordingly
    suit = suitArr[random.Next(0,4)];       // choose suit string from array
    card1.setSuit(suit);                    // set card suit
    card2.setVal(random.Next(1,14));        // rinse, repeat for card2...
    val2 = determineFace(card2.getVal());    
    suit = suitArr[random.Next(0,4)];        
    card2.setSuit(suit);  

    // check if same card is drawn twice:

    catchDuplicate(ref card1, ref card2, ref sameCard); 
}
Console.WriteLine ("Player: {0} of {1}", val1, card1.getSuit());
Console.WriteLine ("Computer: {0} of {1}", val2, card2.getSuit());

// compare card values, display winner:

determineWinner(card1, card2);   

所以这是我的问题:

  • 我可以在 Main() 中使用循环并仍然认为它是模块化的吗?
  • 抽卡过程是否写得好/包含正确?
  • 在方法中打印消息是否被认为是不好的做法(即:)determineWinner()

我只编程了两个学期,我想在这个阶段养成良好的习惯。任何输入/建议将不胜感激。

编辑:

catchDuplicate() 现在是一个布尔方法,调用如下所示:

sameCard = catchDuplicate(card1, card2);

感谢@Douglas。

4

4 回答 4

3

如果您的家庭作业的主要问题是创建模块化应用程序,则必须将所有逻辑封装在专门的类中。每个班级只能做一项工作。使用卡片的函数必须属于卡片类。抽卡的功能,应该是另一个类。

我认为这是你作业的目标,祝你好运!

于 2012-04-07T20:19:45.027 回答
3

我可以在 Main() 中使用循环并仍然认为它是模块化的吗?

是的你可以。然而,Main在 OOP 程序中,通常只包含少数启动核心功能的方法调用,然后将其存储在其他类中。

抽卡过程是否写得好/包含正确?

部分。如果我正确理解了您的代码(您只显示Main),那么您会采取一些行动,当以错误的顺序或错误的值完成时,可能不会很好地结束。可以这样想:如果您出售您的类库(不是整个产品,而只是您的类),那么对于没有经验的用户来说,使用您的类库最清晰的方法是什么?

即,考虑一个Deck包含一副纸牌的类。在创建时,它会创建所有卡片并将其洗牌。Shuffle当你的类的用户需要洗牌并添加DrawCard处理发牌等方法时,给它一个洗牌的方法。

进一步:您有不包含在自己的类中的方法,但具有在类中更好的功能。即,determineFace更适合作为类上的方法Card(假设card2是 type Card)。

在方法中打印消息是否被认为是不好的做法(即:defineWinner())?

是和不是。如果您只想在测试期间显示消息,请使用Debug.WriteLine. 在生产版本中,这些将是无操作的。但是,当您在生产版本中编写消息时,请确保从方法名称中可以清楚地看到这一点。即,WriteWinnerToConsole什么的。

这样做更常见,因为:您将打印什么格式的信息?应该附带什么文字?您如何处理本地化?但是,当您编写程序时,显然它必须包含将内容写入屏幕(或表单或网页)的方法。为此,它们通常包含在特定的类中。例如,这可能是类CardGameX

总体思路
想想“一个方法/函数应该只有一个任务,只有一个任务,不应该有副作用(比如计算平方打印,那么打印就是副作用)”的原则。

类的原则是非常高级的:一个类包含逻辑上属于一起并在同一组属性/字段上操作的方法。一个相反的例子:Shuffle不应该是 class 中的方法Card。但是,它在逻辑上属于 class Deck

于 2012-04-07T20:39:27.697 回答
2

对“最佳实践”的所有建议持保留态度。永远为自己着想。

那说:

  • 我可以在 Main() 中使用循环并仍然认为它是模块化的吗?

这两个概念是独立的。如果您的 Main() 只执行高级逻辑(即调用其他方法),那么它是否在循环中这样做并不重要,毕竟算法需要一个循环。(你不会不必要地添加一个循环,不是吗?)

根据经验,如果可能/可行,请让您的程序自我记录。让它“可读”,这样,如果一个新人(甚至几个月后的你)看到它,他们可以在任何层面上理解它。

  • 抽卡过程是否写得好/包含正确?

不。首先,一张牌永远不应该被选中两次。对于更“模块化”的方法,我会有这样的东西:

while ( Deck.NumCards >= 2 )
{
   Card card1 = Deck.GetACard();
   Card card2 = Deck.GetACard();
   PrintSomeStuffAboutACard( GetWinner( card1, card2 ) );
}
  • 在方法中打印消息是否被认为是不好的做法(即:defineWinner())?

determineWinner打印消息的目的吗?如果答案是“否”,那么这不是“坏习惯”的问题,你的功能是完全错误的。

也就是说,存在“调试”构建和“发布”构建之类的东西。为了帮助您调试应用程序并确定哪些有效,哪些无效,添加日志消息是一个好主意。

确保它们是相关的并且它们没有在“发布”构建中执行。

于 2012-04-07T20:36:05.263 回答
1

问:我可以在 Main() 中使用循环并仍然认为它是模块化的吗?

答:是的,您可以使用循环,这对模块化没有真正的影响。

问:抽卡过程写得好/包含正确吗?

A:如果你想更模块化,把 DrawCard 变成一个函数/方法。也许只写 DrawCards 而不是 DrawCard,但是那里有一个优化与模块化的问题。

问:在方法中打印消息是否被认为是不好的做法(即:determineWinner())?

答:我不会说在方法中打印消息是不好的做法,它只取决于上下文。理想情况下,游戏本身只处理游戏逻辑。程序可以有某种游戏对象,它可以从游戏对象中读取状态。这样,您可以在技术上将游戏从基于文本的游戏更改为图形游戏。我的意思是,这对于模块化来说是理想的,但考虑到最后期限,它可能并不实用。您总是必须决定何时必须牺牲最佳实践,因为没有足够的时间。可悲的是,这种情况经常发生。

将游戏逻辑与其呈现分开。对于像这样的简单游戏,这是不必要的依赖。

于 2012-04-07T20:23:58.287 回答