Tuesday, November 10, 2015

The Singleton design pattern

Last few weeks were quite busy so I did not have time to blog. Coming back, I would like to warm up with a quick and easy design pattern to add to the tutorial series, the Singleton design pattern. The Singleton design pattern is a creational one and is used to ensure that a single instance of a class is created and used by different applications, threads and similar. The singleton class has only one instance and provides global access to it.

To showcase this pattern we will imagine a poker table in our brand new 'Geek Casino'. A single deck of cards is used from which different players (in our case threads) are dealt.

First let's create a Card object which will be used by our Singleton CardsDeck later on. The card POJO is very simple, just the card number and the french suit (sidenote: here you can learn more about the origin of the french suit symbols)
package com.tasosmartidis.design_patterns_tutorial.singleton;

public class Card {

    private int number;
    private String frenchSuit;
    
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        if(number <13 && number>0)
            this.number = number;
    }
    public String getFrenchSuit() {
        return frenchSuit;
    }
    public void setFrenchSuit(String frenchSuit) {
        this.frenchSuit = frenchSuit;
    }
    
    @Override
    public String toString() {
        return number + "-of-" +  frenchSuit;
    }
    
    public int compare(Card c1, Card c2) {
        return c1.number - c2.number;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((frenchSuit == null) ? 0 : frenchSuit.hashCode());
        result = prime * result + number;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Card other = (Card) obj;
        if (frenchSuit == null) {
            if (other.frenchSuit != null)
                return false;
        } else if (!frenchSuit.equals(other.frenchSuit))
            return false;
        if (number != other.number)
            return false;
        return true;
    }

}

Next we create the singleton class (i.e., CardsDeck) by making its constructor private and providing a static method the provides the unique instance of the class in a synchronized block so that it is thread safe. We have a few methods to help us with our casino example such as initializing the deck, dealing cards and similar. The source code of the singleton class is presented below:
package com.tasosmartidis.design_patterns_tutorial.singleton;

import java.util.ArrayList;
import java.util.Random;

public class CardsDeck {

    private static CardsDeck uniqueInstance = null;
    
    private ArrayList<Card> deckOfCards = createCardDeck();
    
    static boolean firstThread = true;
    
    // private non-accessible constructor
    private CardsDeck() {}
    
    // the static method that provides the unique instance of the class at any point of the execution
    public static CardsDeck getInstance() {
        
        if(uniqueInstance==null) {
            
            // make the access to the singleton thread-safe
            synchronized(CardsDeck.class) {
                if(uniqueInstance==null)
                    uniqueInstance = new CardsDeck();           
            }
        }
        
        return uniqueInstance;
    }
    
    public ArrayList<Card> getCardsOfDeck() {
        return uniqueInstance.deckOfCards;
    }
    
    public ArrayList<Card> getCardsForPlayer(int numberObCards) {
        ArrayList<Card> playersCards = new ArrayList<>();
        Random random = new Random();
        
        for(int i=0;i<=numberObCards;i++) {
            // Just provide index of a card within range 5-14 to deal to the player. 
            // We assume there would be at least 10 cards in the deck always to keep code simple 
            int cardIndex = random.nextInt(15-5) + 5;
            
            playersCards.add(uniqueInstance.deckOfCards.remove(cardIndex));
        }
        
        return playersCards;
    }
    
    // Methods to initialize our deck of cards to play with
    private ArrayList<Card> createCardDeck() {
        ArrayList<Card> deckOfCards = new ArrayList<>();
        
        String[] frenchSuits = new String[]{"hearts","diamonds","spades","clubs"};
        
        for(String suit : frenchSuits) {
            for(int i=1;i<13;i++) {
                Card newCard = new Card();
                newCard.setFrenchSuit(suit);
                newCard.setNumber(i);
                deckOfCards.add(newCard);
            }
        }
        
        return deckOfCards;
    }
}

Next, we will create a Runnable for dealing cards to player so that later in our demo we can ask cards from the deck via different threads. The GetCardsRunnable implements the Runnable interface as shown below:
package com.tasosmartidis.design_patterns_tutorial.singleton;

import java.util.ArrayList;


public class GetCardsRunnable implements Runnable {

    private String runnableName;
    
    public GetCardsRunnable(String runnableName) {
        this.runnableName = runnableName;
    }
    
    @Override
    public void run() {
        CardsDeck cardsDeck = CardsDeck.getInstance();
        
        System.out.println("Casino ID: " + System.identityHashCode(cardsDeck));
        System.out.println(cardsDeck.getCardsOfDeck());
        
        ArrayList<Card> playerOneCards = cardsDeck.getCardsForPlayer(5);
        System.out.println("Player '" + this.runnableName + "' has: " + playerOneCards);
        
        System.out.println("Cards were dealt!");
        System.out.println();
        
    }

}

Now we can create a simple main method where we will use two of the above Runnables to deal cards from the deck and evaluate how our singleton class works:
package com.tasosmartidis.design_patterns_tutorial.singleton;


public class Casino {

    public static void main(String[] args) {
        Runnable player1Thread = new GetCardsRunnable("player1");
        Runnable player2Thread = new GetCardsRunnable("player2");
        
        new Thread(player1Thread).start(); 
        new Thread(player2Thread).start();
    }

}


Finally let's how our Singleton will work for two players (threads) asking to be dealt by the deck. It should be the same deck, so the cards dealt for the first player should not be available when dealing the second. And indeed as you can see in the figure below, cards dealt to the first player (e.g., 8 of hearts) are not in the deck when dealing to the second.

The 'Casino ID' also shows the hashcode of the CardsDeck object, which as the reader can see is the same. This concludes our short tutorial on the Singleton design pattern. The code is available on GitHub. Comments or questions are welcomed and more tutorials are coming!

No comments:

Post a Comment