• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Switch Case and enum

 
Ranch Hand
Posts: 1402
3
Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Apart from what you have already been told for about two weeks on this thread?



I use to start writing tests in TDD, and building from there... When in the thread we talked about design, I interpreted it as a preliminary work before coding, lets say an UML. Or maybe another different approach...on top of the already commented
 
Saloon Keeper
Posts: 15510
363
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Whatever approach you take, you should always start with a design. You can't write tests or code unless you know what to test and what to implement.

A good way to start a design is to distill nouns and verbs from the problem description. Let's just write nouns down in singular and starting with a capital letter, so they reflect possible class names. Let's write verbs down in infinitive form and starting without a capital letter, so they reflect possible method names. I'm ignoring verbs and nouns that don't have to do with the description of the game.

  • Game
  • Player
  • Strategy
  • win
  • Round
  • Scissors
  • beats
  • Paper
  • Rock
  • choose
  • Tie

  • Now, let's take a look at the nouns and see how they related.

    The game consists of 100 rounds of above two players competing.


    Ah, so a Game has Rounds, and a Round has Players. Players can compete() each other, so the Round has an Outcome. We can make this into an UML diagram, but for now I'll just use a little class outline:


    Now, what does an Outcome look like? Either one of the Players wins, or it's a Tie. Since a Tie is a kind of an Outcome, we don't need to make a class for Tie. How do we determine what the kind of an Outcome is? An Outcome is determined by what a Player chooses, either Rock, Paper, or Scissors. We see that Rock, Paper and Scissors are all specific instances of a Choice, so we don't need to make separate classes. They are also the only instances of Choice, so we can make Choice an enum. Here's are the outlines:


    Hold on now! We see that both Round and Outcome hold Players, and they're both the same. In fact, Players are always the same for an entire Game. So we'll just let the game keep track of Players, and remove them from the Round and the Outcome classes. Now that Round is only a wrapper for an Outcome, we can get rid of Round, and let Game keep track of Outcomes rather than Rounds. Outcome still needs access to the Players though, but we can solve this by making it an inner class of Game.


    We have another problem. Because an Outcome is now associated with an instance of a Game, Player.compete() needs access to an instance of a Game to create Outcomes. This means we have a circular dependency between Player and Game. We can break this circle by making Game responsible for determining the Outcome of a round. Let's remove compete() from Player, and let's add playRound() to Game. This has the added benefit that we don't need the 'Player other' parameter, because Game already knows who the players are. We can also remove the return type, because the method can add the outcome directly to the list of outcomes.


    Now, you have plenty to work with. First, write more Javadoc for the remaining classes and methods. Describe what a method does, what its parameters mean and what it should return. You should also document exceptions that you expect a method to throw. For instance, you should document that the Outcome.getWinner() method throws an IllegalStateException if isTie() returns true.

    After you've written documentation, you can write unit tests for the expected behavior of your classes and methods. You may have to add additional methods and constructors to your classes to be able to finish the unit tests. You should also document these additional methods and constructors.

    Don't worry about the complete assignment yet, like outputting wins and losses. After you're done writing documentation and tests, show us what you've done, and then we can design the next stage of the application. Your unit tests may fail, but they must compile. Do NOT implement any of the methods yet. You can start implementing when you're happy with your design, and you have finished your unit tests.
     
    Creator of Enthuware JWS+ V6
    Posts: 3411
    320
    Android Eclipse IDE Chrome
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Congratulations Isaac Ferguson !!

    This question has been selected for the September Journal

    Cow!
     
    Angus Ferguson
    Ranch Hand
    Posts: 1402
    3
    Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    For instance, you should document that the Outcome.getWinner() method throws an IllegalStateException if isTie() returns true.



    Why? When isTie() returns true, it should thrown anything, isnt it?


    After you've written documentation, you can write unit tests for the expected behavior of your classes and methods. You may have to add additional methods and constructors to your classes to be able to finish the unit tests. You should also document these additional methods and constructors.



    Till now I have just wrote the basic javadoc and tests, before go further.











    For the exceptions, I dont find a clear way to know which one, should it thrown. It looks pretty simple till now. Any comments till this point?

    Regards, Isaac



     
    Stephan van Hulst
    Saloon Keeper
    Posts: 15510
    363
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Isaac Ferguson wrote:Why? When isTie() returns true, it should thrown anything, isnt it?


    No, when isTie() is true for an Outcome, the getWinner() and getLoser() methods should throw an IllegalStateException, because there is no point in calling those methods when a round is tied. They have to throw an exception to let the programmer know they did something silly.

    For the exceptions, I dont find a clear way to know which one, should it thrown.


    Then you should study exceptions more. Important exceptions that you can use in your applications include the following:
    ExceptionWhen?
    IllegalArgumentExceptionThe client passed invalid arguments to a method.
    IndexOutOfBoundsExceptionSame as IllegalArgumentException, except for arguments that are used as indices.
    IllegalStateExceptionThe client tried to call a method that may not be called for the current object state.
    UnsupportedOperationExceptionThe method is not supported yet, or never will be.
    AssertionErrorSomething happened that really should never happen.

    An example of where you can use AssertionError is when a piece of code should be unreachable, but the compiler can't determine that:

    Any comments till this point?


    Your unit tests actually don't describe what they test, and they don't test different scenarios. For instance, you should methods that test that:

  • the playRound() method adds an Outcome to the list,
  • an Outcome created from two ROCKs is a tie,
  • an Outcome created from two PAPERs is a tie,
  • an Outcome created from two SCISSORS is a tie,
  • the getWinner() method throws an IllegalStateException when the outcome is a tie,
  • the getWinner() method returns playerA when playerAChoice beats playerBChoice,
  • the getWinner() method returns playerB when playerBChoice beats playerAChoice,
  • the getLoser() method throws an IllegalStateException when the outcome is a tie,
  • the getLoser() method returns playerB when playerAChoice beats playerBChoice,
  • the getLoser() method returns playerA when playerBChoice beats playerAChoice,


  • For example, here is an example of a test method:

    Your Javadoc should explain what real-life concepts your classes represent. For instance, the Javadoc for the Choice enum can just read "A choice that a player can make in a game of Rock-Paper-Scissors".

    There is no point in putting @param, @return and @throws clauses in type descriptions, or in descriptions of methods that don't have parameters, a return type or exceptions, respectively.

    Your @param descriptions for the Game constructor don't make sense: there are no types playerA and playerB.

    Why are you initializing the outcomes list from a constructor parameter?

    Why have you implemented the constructors? First write the documentation and tests, then implement your classes.
     
    Angus Ferguson
    Ranch Hand
    Posts: 1402
    3
    Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Your @param descriptions for the Game constructor don't make sense: there are no types playerA and playerB.



    So like this should be correct ?



    Why are you initializing the outcomes list from a constructor parameter?



    When I dont do it, I get compilation errors like:



    Any advice, please?
     
    Marshal
    Posts: 79178
    377
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Isaac Ferguson wrote:. . .
    So like this should be correct ?

    . . .

    Is that supposed to be part of a documentation comment? If so, it is incorrect. You shou‍ld have a separate @param tag for each parameter, and it shou‍ld be followed by the parameter name and a brief description. More details here. You would want something like this:-And iff is a real word in computer sciences.
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 15510
    363
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Isaac Ferguson wrote:So like this should be correct ?


    No. Like I said earlier, you need to describe things as if they're real world objects. You should document them like so:

    When I dont do it, I get compilation errors like:


    You are treating symptoms, but you're not looking at the broader picture. How many outcomes does a new game of Rock-Paper-Scissors have? Zero. Does it need the outside world to tell it that? No. That means you can directly instantiate the list, without a constructor parameter.
     
    Angus Ferguson
    Ranch Hand
    Posts: 1402
    3
    Netbeans IDE Spring Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Is there any benefit in doing it final? It cant be extended, but there is any reason for it?

     
    Stephan van Hulst
    Saloon Keeper
    Posts: 15510
    363
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    When you write a class, it should be a habit to make it final. Only remove the final modifier if you've thought long and hard that you want to be able to override a class, because making a class properly extensible is actually not a very easy task.

    If you want to create different types of players that use different kinds of choosing strategies, you could make the Player class abstract.

    However, let's look back at that sentence: "Players that use strategies". Strategy is a noun, meaning you could make it a property of a Player, and let the choose() method delegate to it. Delegation is almost always better than inheritance, and in this particular case allows us to write a functional interface. When you can write an interface and avoid an abstract class, you should almost always do it:
     
    Don't get me started about those stupid light bulbs.
    reply
      Bookmark Topic Watch Topic
    • New Topic