Translating Class Diagrams into Code
Part One showed a process for developing and refining class diagrams. This part is going to show how those diagrams translate into code. The code is shown in this blog, but is also available on Github.
Let's start with the Card class. It is fairly simple and is core to everything that follows. Here is the diagram of Card and of the enumeration that was added to store the Suits.
First here is the code for the enumeration:
package com.spconger; /* this enum lists the * four card suits */ public enum Suits { HEARTS, DIAMONDS, CLUBS, SPADES; }
Now here is the class Card. It consists of the two fields, plus gets and sets for those fields. It also has an overloaded constructor which did not appear in the class diagram, but could have.
package com.spconger; public class Card { /*this class defines a card. A card * has a name, a suit (which comes from * the enum and a value of 1 to 11. * It could also have a path to the card * picture, but that is for later. * I have included two constructors * an empty one and one that takes in * all three values. I have also included * gets and sets for all the fields. * This allows for flexibility, in how * the class is accessed and used. */ private Suits suit; private int cardNumber; //empty constructor public Card(){} //over loaded constructor public Card(Suits suit, int cardNumber){ this.suit=suit; this.cardNumber=cardNumber; } //sets and gets public Suits getSuit() { return suit; } public void setSuit(Suits suit) { this.suit = suit; } public int getCardNumber() { return cardNumber; } public void setCardNumber(int value) { this.cardNumber = value; } }
Now let's look at the Class Deck.
Deck and Card have a Composition relationship. One way to look at it is that a Deck is composed of Cards. We are assuming that if the Deck is destroyed the cards go with it. Deck has one field which is an ArrayList of cards. In the diagram it has two methods: a private method to populate the deck and a public method to shuffle it. Here is the code.
package com.spconger; import java.util.ArrayList; import java.util.Collections; public class Deck { /* * A deck is essentially an ArrayList * of cards with a method to populate * the array with the usual 52 * card deck, a method to shuffle * the deck, and a method that returns * a copy of the deck */ private ArrayList<Card> deck; //constructor initializes the deck public Deck(){ deck=new ArrayList<Card>(); populateDeck(); } //populates the deck. The suit //uses the enum Suits private void populateDeck(){ for(Suits suit : Suits.values()){ for(int i = 1;i<=13;i++){ Card card = new Card(); card.setSuit(suit); card.setCardNumber(i); deck.add(card); } } } //shuffles the deck public void shuffleDeck(){ Collections.shuffle(deck); } //returns a copy of the deck public ArrayList<Card> getDeck(){ return deck; } }
Again Deck has a constructor not shown in the diagram. It is used to do two things: initialize the ArrayList, and populate the deck. You might also notice that there is an additional method not shown in the diagram, a getDeck() method. I added this after I realized I would need it sometimes. I can add this to the diagram.
It is important to know that class diagrams are not immutable. As you get into the coding and further development you may often find that you need to make changes to the original class design. It is also important that you update the class diagrams and document the changes.
Next let's look at Game. It is an abstract class, with two fields: a Deck, and a string description of the game. It implements the Interface IGame, but, because it is abstract, does not have to do the actual implementation in itself. First we will look at the interface.
package com.spconger; public interface IGame { Card deal(); void turn(Player player); int turnResult(); }
Again the interface is just method signatures. The classes the implement the interface MUST have those methods.
Here is the game class.
package com.spconger; public abstract class Game implements IGame{ /* * This as an abstract class from which * each card game type will inherits * It also implements the interface * IGame. Because this class is abstract * it does not have to implement * the interface methods, but all * it's children do. */ //every game will have a deck protected Deck deck; //every game should have a description private String gameDescription; //sets and gets for the fields public Deck getDeck() { return deck; } public void setDeck(Deck deck) { this.deck = deck; } public String getGameDescription() { return gameDescription; } public void setGameDescription(String gameDescription) { this.gameDescription = gameDescription; } public String toString(){ return "Put name of the game here"; } }
Again the code contains gets and sets for the field. I also added a method to the Class
public String toString(){ return "Put name of the game here"; }
This is an override-able method. I found I need it to identify the actual type of game being played. It also should be added to the class diagram.
Next let's look at an actual game that inherits from Game. In the code I only did BlackJack as the example and test of concept. Here is the code for BlackJack.
package com.spconger; import java.util.ArrayList; public class BlackJack extends Game{ /* * This class extends Game, and * because Game implements * the interface IGame, BlackJack must * implement and give a body to those * methods. */ private ArrayList<Card> myDeck; //constructor for initializing elements public BlackJack(){ deck = new Deck();; deck.shuffleDeck(); myDeck=deck.getDeck(); } //methods from interface @Override public Card deal() { Card card = myDeck.get(0); myDeck.remove(card); return card; } @Override public void turn(Player player) { // TODO Auto-generated method stub } @Override public int turnResult() { // TODO Auto-generated method stub return 0; } //returns the name of the game @Override public String toString(){ return "BlackJack"; } }
All the methods override methods from Game. Notice that all the methods from the interface IGame are present, even though I haven't given them all a body. We have also overridden the toString() method to announce which game this is. BlackJack also has a fairly complex constructor. It initializes a deck, shuffles it, and gets a local copy of it.
Only two classes remain: Player and Hand. Since every player has a Hand, let's start with it. A Hand consists of an ArrayList of cards and methods to add and remove cards from the list. Here is the diagram and the code.
package com.spconger; import java.util.ArrayList; public class Hand { /* * a hand is an ArrayList of cards * taken from a deck, * the number of cards depends * on the game. * There are methods to add * and remove cards * and a method to return the whole * hand */ private ArrayList<Card> cards; //constructor public Hand(){ cards= new ArrayList<Card>(); } public void addCard(Card c){ cards.add(c); } public void RemoveCard(Card c){ cards.remove(c); } public ArrayList<Card> getHand(){ return cards; } }
I also added a method to return or show the Hand
The player has a name, a Hand and points. Currently there is only one method play(). I suspect that for the actual application this class would need a great deal more development. In fact, my player class diverges from the diagram quite a bit. Here is the original diagram and code as it is.
package com.spconger; public class Player { /* * This class represents a player * it is certainly not complete * The constructor takes in a generic * Game class. Of course the Game * class is abstract and so cannot be * instantiated directly, but any * child can substitute for a parent, * though to get its full content * you have to cast it back to its * true type. */ private Hand hand; private String name; private int points; private Game g; //constructor with abstract Class as parameter public Player(Game g){ hand = new Hand(); //check on the games type using //its toString function and //initialize it as that type if(g.toString().equals("BlackJack")){ this.g=new BlackJack(); } } //sets and gets public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPoints() { return points; } public void setPoints(int points) { this.points = points; } //method to populate a hand public void setHand(int number){ for(int i=0; i<number; i++){ Card c = g.deal(); hand.addCard(c); } } public Hand showHand(){ return hand; } }
I don't have a play method at all in the code. Instead I have a method to populate the hand and a method to show the hand. I also added a constructor that takes the type Game as a parameter. Game is an abstract class and cannot be instantiated, but its children can be. It is an important concept in Object Oriented Programming that in Inheritance, a child can always be substituted for the parent. Note when I run the code later I pass in BlackJack for the Game parameter. It is also necessary to cast the child to the actual type it is, if you are going to be able to access all the child's methods and fields. The adjusted class diagram would look like this (though I probably should have added the constructor):
One last piece remains, to do a minimal test of the classes. My goal is simply to deal a hand of cards to two players. For BlackJack an initial hand should consist of two cards. In main I wrote this test code.
package com.spconger; import java.util.ArrayList; public class Program { public static void main(String[] args) { BlackJack bj = new BlackJack(); Player p1 = new Player(bj); p1.setName("Player One"); p1.setHand(2); Hand h = p1.showHand(); ArrayList<Card> cards =h.getHand(); System.out.println(p1.getName()); for(Card c : cards) { System.out.println(c.getSuit() + ": " + c.getCardNumber()); } Player p2 = new Player(bj); p2.setName("Player Two"); p2.setHand(2); Hand h2 = p2.showHand(); ArrayList<Card> cards2 =h2.getHand(); System.out.println(p2.getName()); for(Card c2 : cards2) { System.out.println(c2.getSuit() + ": " + c2.getCardNumber()); } /* ArrayList<Card> cards = d.getDeck(); for(Card c: cards){ System.out.println(c.getSuit() + ": " + c.getValue()); } System.out.println();; d.SortDeck(); ArrayList<Card> shuffled = d.getDeck(); for(Card c: shuffled){ System.out.println(c.getSuit() + ": " + c.getValue()); } */ } }
I created two players. For each of them I set their hand and then returned it so I could loop through it and display the cards. You might also note the commented out code where I tested other aspects at different times.
Here is the result of the program when run:
No comments:
Post a Comment