zoukankan      html  css  js  c++  java
  • 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(五)

    0. #include <stdio.h>
    1. #include <time.h>
    2. #include <ctype.h>
    3. #include <stdlib.h>
    4. 
    5. #define BELL 'a'
    6. #define DEALER 0
    7. #define PLAYER 1
    8. 
    9. #define ACELOW 0
    10. #define ACEHIGH 1
    11. 
    12. int askedForName = 0;
    13. 
    14. 
    15. void dispTitle(void);
    16. void initCardsScreen(int cards[52],int playerPoints[2],
    17. int dealerPoints[2], int total[2], 
    18. int *numCards);
    19. int dealCard(int * numCards,int cards[52]);
    20. void dispCard(int cardDrawn,int points[2]);
    21. void totalIt(int points[2],int tatal[2],int who);
    22. void dealerGetsCard(int *numCards,int cards[52],
    23. int dealerPoints[2]);
    24. void playerGetsCard(int *numCards,int cards[52],
    25. int playerPoints[2]);
    26. char getAns(char mesg[]);
    27. void findWinner(int total[2]);
    28. 
    29. main()
    30. {
    31.    int numCards;
    32.    int cards[52],playerPoints[2],dealerPoints[2],total[2];
    33.    char ans;
    34.    
    35.    do 
    36.    { 
    37.       initCardsScreen(cards,playerPoints,dealerPoints,total, &numCards);
    38.       dealerGetsCard(&numCards,cards, dealerPoints);
    39.       printf("
    ");
    40.       playerGetsCard(&numCards,cards,playerPoints); 
    41.       playerGetsCard(&numCards,cards,playerPoints);
    42.       do
    43.       {
    44.          ans = getAns("Hit or stand (H/S)?");
    45.          if ( ans == 'H' )
    46.          { 
    47.             playerGetsCard(&numCards,cards,playerPoints);
    48.          }  
    49.       }
    50.       while( ans != 'S' );
    51.       
    52.       totalIt(playerPoints,total,PLAYER);
    53.       do
    54.       {
    55.          dealerGetsCard(&numCards,cards,dealerPoints);
    56.       }
    57.       while (dealerPoints[ACEHIGH] < 17 );
    58.       
    59.       totalIt(dealerPoints,total,DEALER);
    60.       findWinner(total); 
    61.       
    62.       ans = getAns("
    Play again(Y/N)?");  
    63.    }
    64.    while(ans=='Y');
    65.    
    66.    return 0;
    67. }
    68. 
    69. void initCardsScreen( int cards[52],int playerPoints[2],
    70.                       int dealerPoints[2], int total[2], 
    71.                       int *numCards )
    72. {
    73.    int sub,val = 1 ;
    74.    char firstName[15];
    75.    *numCards=52;
    76.    
    77.    for(sub=0;sub<=51;sub++)
    78.    {
    79.       val = (val == 14) ? 1 : val;
    80.       cards[sub] = val;
    81.       val++;  
    82.    }
    83.    
    84.    for(sub=0;sub<=1;sub++)
    85.    { 
    86.       playerPoints[sub]=dealerPoints[sub]=total[sub]=0;
    87.    }
    88.    dispTitle();
    89.    
    90.    if (askedForName==0)
    91.    { 
    92.       printf("What is your first name?");
    93.       scanf(" %s",firstName);
    94.       askedForName=1;
    95.       printf("Ok, %s,get ready for casino action!
    
    ",firstName);
    96.       getchar();
    97.    }
    98.    return;        
    99. }
    100. 
    101. void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
    102. {
    103.    int newCard;
    104.    newCard = dealCard(numCards, cards);
    105.    printf("You draw:");
    106.    dispCard(newCard,playerPoints);
    107. }
    108. 
    109. 
    110. void dealerGetsCard(int *numCards,int cards[52],int dealerPoints[2])
    111. {
    112.    int newCard;
    113.    newCard = dealCard(numCards,cards);
    114.    printf("The dealer draws:");
    115.    dispCard(newCard,dealerPoints);
    116. }
    117. 
    118. int dealCard(int * numCards,int cards[52])
    119. {
    120.    int cardDrawn,subDraw;
    121.    time_t t;
    122.    srand(time(&t));
    123.    subDraw = (rand()%(*numCards));
    124.    cardDrawn = cards[subDraw];
    125.    cards[subDraw] = cards[*numCards -1];
    126.    (*numCards)--;
    127.    return cardDrawn;
    128. }
    129. 
    130. void dispCard(int cardDrawn, int points[2])
    131. {
    132.    switch(cardDrawn)
    133.    {
    134.       case(11): printf("%s
    ","Jack");
    135.                 points[ACELOW] += 10;
    136.                 points[ACEHIGH] += 10;
    137.                 break;
    138.       case(12): printf("%s
    ","Queen");
    139.                 points[ACELOW] += 10;
    140.                 points[ACEHIGH] += 10;
    141.                 break;
    142.       case(13): printf("%s
    ","King");
    143.                 points[ACELOW] += 10;
    144.                 points[ACEHIGH] += 10;
    145.                 break;
    146.       default : points[ACELOW] += cardDrawn;
    147.                 if(cardDrawn==1)
    148.                 { 
    149.                    printf("%s
    ","Ace");
    150.                    points[ACEHIGH]+= 11;
    151.                 }
    152.                 else
    153.                 {  
    154.                   points[ACEHIGH]+=cardDrawn;
    155.                   printf("%d
    ",cardDrawn); 
    156.                 }
    157.    }
    158.    return ;
    159. }
    160. 
    161. void totalIt(int points[2],int total[2],int who)
    162. {
    163.    if ( (points[ACELOW] == points[ACEHIGH])
    164.       ||(points[ACEHIGH] < 21 ))
    165.    { 
    166.      total[who] = points[ACELOW];
    167.    }
    168.    else
    169.    { 
    170.        total[who] = points[ACEHIGH];
    171.    }
    172.    
    173.    if (who == PLAYER )
    174.    {
    175.       printf("You have a total of %d
    
    ", total[PLAYER]);
    176.    }
    177.    else
    178.    {
    179.        printf("The house stands with a total of %d
    
    ", 
    180.        total[DEALER]);
    181.    }
    182.    return;
    183. }
    184. 
    185. void findWinner(int total[2])
    186. {
    187.    if ( total[DEALER] ==  21 )
    188.    {
    189.        printf("The house wins.
    ");
    190.        return ;
    191.    }
    192.    if ( (total[DEALER] > 21) && (total[PLAYER] > 21) )
    193.    { 
    194.       printf("%s", "Nobody wins.
    ");
    195.       return ; 
    196.    }
    197.    if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21))
    198.    { 
    199.       printf("The house wins.
    ");
    200.       return ; 
    201.    }
    202.    printf("%s%c","You win!
    ",BELL);
    203.    return;
    204. }
    205. 
    206. char getAns(char mesg[])
    207. {
    208.    char ans;
    209.    printf("%s", mesg);
    210.    ans = getchar();
    211.    getchar();
    212.    return toupper(ans);
    213. }
    214. 
    215. void dispTitle(void)
    216. {
    217.    int i = 0 ;
    218.    while(i<25)
    219.    { 
    220.         printf("
    ");
    221.         i++; 
    222.    }
    223.    printf("
    
    *Step right up to the Blackjack tables*
    
    ");
    224.    return ;
    225. }
    View Code

      继续走查dispCard()函数:  

    20. void dispCard(int cardDrawn,int points[2]);
    
    130. void dispCard(int cardDrawn, int points[2])
    131. {
    132.    switch(cardDrawn)
    133.    {
    134.       case(11): printf("%s
    ","Jack");
    135.                 points[ACELOW] += 10;
    136.                 points[ACEHIGH] += 10;
    137.                 break;
    138.       case(12): printf("%s
    ","Queen");
    139.                 points[ACELOW] += 10;
    140.                 points[ACEHIGH] += 10;
    141.                 break;
    142.       case(13): printf("%s
    ","King");
    143.                 points[ACELOW] += 10;
    144.                 points[ACEHIGH] += 10;
    145.                 break;
    146.       default : points[ACELOW] += cardDrawn;
    147.                 if(cardDrawn==1)
    148.                 { 
    149.                    printf("%s
    ","Ace");
    150.                    points[ACEHIGH]+= 11;
    151.                 }
    152.                 else
    153.                 {  
    154.                   points[ACEHIGH]+=cardDrawn;
    155.                   printf("%d
    ",cardDrawn); 
    156.                 }
    157.    }
    158.    return ;
    159. }

       dispCard()函数的功能是显示抽到的牌的点数并计算抽牌者目前的总点数。这个函数最主要的毛病是可读性差,原因主要有两点,第一,在switch语句中蹩脚地嵌套了一句if语句,实际上这个switch语句可以这样写: 

    void dispCard(int cardDrawn,int points[]);
    
    void dispCard(int cardDrawn, int points[])
    {
       switch(cardDrawn)
       {
          case 11: puts("Jack");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 12: puts("Queen");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 13: puts("King");
                   points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 1  :puts("Ace");
                   points[ACELOW]  += 1 ;
                   points[ACEHIGH] += 11;
                   break;                
          default :printf("%d
    ",cardDrawn);
                   points[ACELOW]  += cardDrawn;
                   points[ACEHIGH] += cardDrawn;
                   break;
       }
    }
    

    显然要好看得多。
      第二,就是不应把输出牌面点数与计算点数放在这一个函数中同时完成,应该分为两个函数。 

    void dispCard(int cardDrawn);
    
    void dispCard(int cardDrawn)
    {
       switch(cardDrawn)
       {
          case 11: puts("Jack");
                   break;
          case 12: puts("Queen");
                   break;
          case 13: puts("King");
                   break;
          case 1  :puts("Ace");
                   break;                
          default :printf("%d
    ",cardDrawn);
                   break;
       }
    }
    
    void update(int cardDrawn,int points[]);
    
    void update(int cardDrawn, int points[])
    {
       switch(cardDrawn)
       {
          case 11: 
          case 12: 
          case 13: points[ACELOW]  += 10;
                   points[ACEHIGH] += 10;
                   break;
          case 1  :points[ACELOW]  += 1 ;
                   points[ACEHIGH] += 11;
                   break;                
          default :points[ACELOW]  += cardDrawn;
                   points[ACEHIGH] += cardDrawn;
                   break;
       }
    }
    

      这样代码更简单。
      现在回到main()函数,考察 dealerGetsCard(&numCards,cards, dealerPoints); 之后的代码。 

    40.       playerGetsCard(&numCards,cards,playerPoints); 
    41.       playerGetsCard(&numCards,cards,playerPoints);

       这两行的意思是在dealer(计算机)取得一张牌后,player开始抽牌。由于player至少要抽两张牌,所以连续两处调用playerGetsCard()函数。然而查看一下这个函数的定义就会发现: 

    24. void playerGetsCard(int *numCards,int cards[52],
    25. int playerPoints[2]);
    
    101. void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
    102. {
    103.    int newCard;
    104.    newCard = dealCard(numCards, cards);
    105.    printf("You draw:");
    106.    dispCard(newCard,playerPoints);
    107. }

       这个函数其实与dealerGetsCard()函数是一样的函数。同样的函数写了两次,无论如何都是很垃圾的写法。

      写代码有一条基本原则——要“拽”DRY(Don’t repeat yourself)(参见http://www.cnblogs.com/pmer/archive/2011/07/16/2108436.html)。相同的函数定义了两次就是不够“拽”。

      由于这两个函数基本相同,所以很容易合并为一个。它们的差别只在 

    105. printf("You draw:");

      

    114.    printf("The dealer draws:");

     这两句,可以通过为函数增加一个参数的办法统而为一,也可以把这两句从函数中直接剥离,这时playerGetsCard()和dealerGetsCard()这两个函数本身都没有存在的必要性了。即在main()中直接可调用dispCard()函数: 

    int main(void)
    {
        /*……*/
        do{
            /*……*/
            printf("The dealer draws:");
            dispCard ( dealCard( numCards , cards ) , dealerPoints ) ;
            
            printf("You draw:");
            dispCard ( dealCard( numCards , cards ) , playerPoints) ;
            /*……*/
        }
        while( getAns("
    Play again(Y/N)?") == 'Y' );  /*询问是否继续*/
        return 0;
    }
    

       main()中的 

    42.       do
    43.       {
    44.          ans = getAns("Hit or stand (H/S)?");
    45.          if ( ans == 'H' )
    46.          { 
    47.             playerGetsCard(&numCards,cards,playerPoints);
    48.          }  
    49.       }
    50.       while( ans != 'S' );

     的作用是让player输入。如果输入为H,则继续抽取一张牌;如果输入为S,则player取牌过程结束;如果输入其他字符,则继续提问。

      这段代码的逻辑有些复杂化。如果我写,大概会按下面方式写:

          do
          {
             char ans;
             
             ans = getAns("Hit or stand (H/S)?");
             
             if ( ans == 'H' )
                 playerGetsCard(&numCards,cards,playerPoints);
             
             if ( ans == ' S ' )
                break;
             
          }
          while( 1 );
    

     并且把它抽象为一个函数。 

  • 相关阅读:
    Blank page instead of the SharePoint Central Administration site
    BizTalk 2010 BAM Configure
    Use ODBA with Visio 2007
    Handling SOAP Exceptions in BizTalk Orchestrations
    BizTalk与WebMethods之间的EDI交换
    Append messages in BizTalk
    FTP protocol commands
    Using Dynamic Maps in BizTalk(From CodeProject)
    Synchronous To Asynchronous Flows Without An Orchestration的简单实现
    WSE3 and "Action for ultimate recipient is required but not present in the message."
  • 原文地址:https://www.cnblogs.com/pmer/p/3181570.html
Copyright © 2011-2022 走看看