Edit Page

Singleton Design Pattern

Slides

Video

Code

DBConnection.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class DBConnection{
  private static DBConnection uniqueInstance;

  // properties or instance variables here
  private int portNumber;
  private String hostName;
  // TODO: Add getters and setters

  private DBConnection(){}

  public static DBConnection getInstance(){
    if (uniqueInstance == null){
      uniqueInstance = new DBConnection();
    }
    return uniqueInstance;
  }
}

Main.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Main{
  public static void main(String[] args){
    // create a single instance
    DBConnection instance1 = DBConnection.getInstance();
    // create another instance to show that it's exactly the same single instance
    DBConnection instance2 = DBConnection.getInstance();
    // Compare object identity to determine whether these two objects share and 
    // reference the same object (same memory address)
    if (instance1 == instance2) {
      System.out.println("ONE single instance of the class was created.");
    }
    else {
      System.err.println("Error: Multiple instances were created!");
    }
  }
}
View the complete source code on GitLab

Usage of the singleton pattern in a real world scenario

The example below creates a singleton class that manages a database connection object for a PostgreSQL database. This example stores a To-do list in the database.

To run this code example, you will need to install and run PostgreSQL, and create a database named tododb

DBConnection.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DBConnection{
  private final String url;
  private final int port;
  private final String dbName;
  private Connection connection;

  private static DBConnection instance;

  private DBConnection() throws SQLException {
    this.dbName = "tododb";
    this.port = 5432;
    this.url = "jdbc:postgresql://localhost:" + Integer.toString(this.port) + "/" + this.dbName;
    Properties props = new Properties();
    props.setProperty("user","postgres");
    props.setProperty("password","secret");
    props.setProperty("ssl","false");
    this.connection = DriverManager.getConnection(url, props);
  }

  public Connection getConnection() {
    return this.connection;
  }

  public static DBConnection getInstance() throws SQLException {
    if(instance == null){
      instance = new DBConnection();
    }
    else if (instance.getConnection().isClosed()) {
      instance = new DBConnection();
    }
    return instance;
  }
}

Task.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import java.sql.*;

public class Task {
    private String name;
    private boolean isComplete;

    public Task(String name, boolean isComplete){
        this.name = name;
        this.isComplete = isComplete;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isComplete() {
        return isComplete;
    }

    public void setComplete(boolean complete) {
        isComplete = complete;
    }

    public void insertTask(){
        try {
            Connection dbConnection = DBConnection.getInstance().getConnection();
            Statement stmt = dbConnection.createStatement();
            PreparedStatement insertStmt =
                    dbConnection.prepareStatement("INSERT INTO todo (task, status) VALUES (?, ?);");
            insertStmt.setString(1, this.name);
            insertStmt.setInt(2, (this.isComplete ? 1: 0));
            int rows = insertStmt.executeUpdate();
            System.out.println("Rows affected: " + rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void retrieveTasks(){
        try {
            Connection dbConnection = DBConnection.getInstance().getConnection();
            Statement stmt = dbConnection.createStatement();
            String query = "SELECT id, task, status FROM todo";
            ResultSet rs = stmt.executeQuery(query);
            while(rs.next()){
                //Display values
                String row = "ID: " + rs.getInt("id") +
                        " Task: " + rs.getString("task") +
                        " Status: " + rs.getInt("status") + "\n";
                System.out.print(row);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    // public void updateTask(){
    //
    //}

    public String toString(){
        return "Task: " + this.name + "\nStatus: " + (this.isComplete ? "1": "0");
    }
}

App.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.PrintStream;
import java.sql.SQLException;

public class App {
    private static PrintStream out;

    public static void main(String[] args) {

        try {
            DBConnection db1 = DBConnection.getInstance();
            DBConnection db2 = DBConnection.getInstance();
            if (db1 == db2) {
                System.out.println("It's a singleton");
            } else {
                System.err.println("Error: Two different objects");
            }

            // Insert
            Task t = new Task("Do the laundry.", false);
            t.insertTask();
            // Retrieve all tasks
            t.retrieveTasks();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}
View the complete source code on GitLab