Link to home
Start Free TrialLog in
Avatar of jkteater
jkteaterFlag for United States of America

asked on

Java - Explain Singleton

I have 2 classes that I was told I will need to merge them together and to look up Singleton.  I have never heard of that before.  I look it up on google and it seems some what complicated.  Can someone explain what it is?
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

It's actually very simple: it's a class of which there can only ever be one instance
This is the classic desing - you have private constructor and static instance getIntsance method where
you check that instance should be zero befire yyou create it - in this way no omne can create two instances of this class in one application


public class ClassicSingleton {
   private static ClassicSingleton instance = null;
   protected ClassicSingleton() {
      // Exists only to defeat instantiation.
   }
   public static ClassicSingleton getInstance() {
      if(instance == null) {
         instance = new ClassicSingleton();
      }
      return instance;
   }
}

Open in new window

What they mean bout merging two classes together - that's another story and can be meant in amny different ways
SO with that design above just to reiterate - you may be sure that wherever you access an instance of thsi calss
in your appliction you will always access one and the same instacne; that is bcecauyse if instance is not null
it will return to getInstance() method already existying instance.
Avatar of jkteater

ASKER

I have a EdiSelection class ( you may remember helping with it ) and I have a SelectedTModel class.

The EdiSelection class creates my ArrayList using a add() that is being called when the user selects objects

 EdiSelection.add(tcRevision,selectedDataset); <- that is called in my EdiHandler class.


EdiSelection Class
 
public class EdiSelection {
	
	static ArrayList<RevDataset> rds = new ArrayList<RevDataset>();
	
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
	public EdiSelection() {
	   // rds = new ArrayList<RevDataset>();
	}// end Constructor

   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                            add()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public static void add(TCComponentItemRevision tcRevision, TCComponentDataset selectedDataset){
      //rds.add(new RevDataset(tcRevision,selectedDataset));
	  //System.out.println("Key :" + tcRevision.toString() + " Value : " + selectedDataset.toString() + " \n");
	   RevDataset pp = new RevDataset(tcRevision,selectedDataset);
	   if(!rds.contains(pp))rds.add(pp);   
   }// end add

   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                        getList()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////	
   public static ArrayList<RevDataset> getList(){
      return rds;	
   }// end GetList
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                        clearList()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////	
   public static ArrayList<RevDataset> clearList(){
      rds.clear();
	  return rds;	
   }// end GetList

} // end class EdiSelection

Open in new window


my  SelectedTModel class basically builds my tablemodel I am using to populate my jtable in my base dialog.
It is using the ArrayList we created in EdiSelection

 
public class SelectedTModel extends AbstractTableModel {
   
   private static final long serialVersionUID = 1L;
   private ArrayList<RevDataset> aList;
   
   static final int ITEMID_COL   = 0;
   static final int REVID_COL    = 1;
   static final int PRL_COL      = 2;
   static final int DATASETNAME_COL  = 3;
   static final int DATASET_COL  = 4;
   
   static final int MAX_COLUMNS  = 5;
     
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public SelectedTModel() {
      super();
      this.aList = EdiSelection.rds;
      
      
                
   } // end constructor
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnCount()                         //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public int getColumnCount() {
	   
      return MAX_COLUMNS;
   } // end getColumnCount()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getRowCount()                            //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////   
   public int getRowCount() {
      return aList.size();
	  
   } // end getRowCount()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getValueAt()                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////  
   public Object getValueAt(int row, int column){
      Object o = null;
           
	  try {	   
	     switch (column) {
	        case ITEMID_COL: { o = aList.get(row).rev.getItem().getStringProperty("item_id"); break; }
	        case REVID_COL: { o = aList.get(row).rev.getStringProperty("item_revision_id"); break; }
	        case PRL_COL: { o = aList.get(row).rev.getRelatedComponent("IMAN_master_form_rev").getStringProperty("PRL"); break; }
	        case DATASETNAME_COL: { o = aList.get(row).componentdataset.getStringProperty("object_string"); break; }
	        case DATASET_COL: { o = aList.get(row).componentdataset.getStringProperty("object_type"); break; }
	     }
	  }
	  catch (Exception e) {
	     e.printStackTrace();
	 
      } 
	  return o;
   }
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnName()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public String getColumnName(int column) {
      String s = null;
      
      switch (column) {
      case ITEMID_COL:   { s = "ItemId"; break; }
      case REVID_COL:    { s = "RevId"; break; }
      case PRL_COL:      { s = "PRL"; break; }
      case DATASETNAME_COL:  { s = "Dataset Name"; break; }
      case DATASET_COL:  { s = "Dataset Type"; break; }
      }
   return s;
   } // end getColumnName()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                         removeSelectedRow()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public void removeSelectedRow(int row) {
	   boolean b = (aList.remove(row) != null);
	   //fireTableDataChanged();
	   fireTableRowsDeleted(row, row);
   } 
  
}//end SelectedTModel

Open in new window


I have been told that I need to merge these 2 classes and use a singleton to do it?

The main problem is there are 2 entries to this program.
1. EdiHeader class - The user selects objects using a shortcut menu and then the objects create the ArrayList
2. EdiBaseDialog class - The user can select a button on the top menu - this opens the base dialog with the jtable

 EdiHeader way calls the EdiSelection class and that is the only way it is called
 EdiBaseDialog way calls the SelectedTModel class and that is only way it is called

so if the user goes in the proper order and selects his objects first shortcut menu( EdiHeader ) and then selects the program button at the top menu ( EdiBaseDialog ) it works great.

but what if the user wants to start the base dialog with a empty table and then shortcut menu his object selections.  The user needs to see the jtable update with his selections.  Today that have to close the base dialog and re open it to show the new selections.

So I was told if we merge the classes it would fix that issue

No, frankly I don't see the connection with merguing the classes.

What you need to have is to establish proper connection between your classes.
So when the user starts the base dialog with an emapty table and then makes selsections in the menu - you jtable should get populated and refreshed
and then they will see it upddated. Ti achive this you don't need to merge them into one class - you just need to mainitain proper handles to
instances of one class in another class and excute methods of easch other when necesssary.
I think we went through similar situations already
we have and I still can not get it to work
Also after the user adds something to the jtable, I can resize the dialog box and it updates the table
If you see the change only after you do resize - it means you need to add repaint() in your code after you made the change
and then you should not need to resize to see the change.
Beciuse that is the reason you see it after resize - it does repaint() when resizing


>we have and I still can not get it to work

You need to understand how these interactions wroek - I;d even suggest that you
for a time loook at some very simple exmples of  interactions between class through these handles - this
is a very importnat part and you need to understand it.
I don't rememeber was it for you that I made this simple example with three windows - one main and two children passing references to each other.
If that was not for you i can post it for you - and try to understand how these interactions work - it is very importnat for any java work
With something like this it's very difficult to help without runnable code
Even though I now know you don't think it will not fix my issue, I would love to learn how to merge those two classes using simpleton - I am curious now
>With something like this it's very difficult to help without runnable code

I very much agree. And it does not look like this is some classical case for singleton.
Your case is not that you cannot have two imnstances of some entity, but rather
that you want to be sure to access this very instance which you need from another place
in your code.
And merging classes does not have direct effect on the flow of operation - you can put bunch of methods in one class or in
different classes - the way your program operates will not necessarily depend on that.
OK - I understand about the runnable code.  
I would suggest that in a spare moment you look at this ismple example of how
classes intreact with each other - you can compile and see
how parent window intercats with childeren and chikldern with each other and look
at the ccode - in this cvase they represent framses - but this intercation throough
the handles between the classes in java application can work the same way between any classes,
not necessarily corresponding to visual elements
and it shows how in another class you can reach the same instance of some class
 which you creted in the third  class.

These things need to be understood clearly, then you'll be able to update
the table you created somewhere form another location in your application.
If you don't uinderstand this stuff, ask questions, it is really important.


import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyMainWindow extends JFrame implements ActionListener {

      JTextField txt;
      JButton button;
      MyChildWindow child1;
      MyChildWindow child2;


    public MyMainWindow(){
        super("main");
     txt = new JTextField(10);
      button = new JButton("press");
        button.addActionListener(this);
            Container c = this.getContentPane();
        c.setLayout(new FlowLayout());
        c.add(txt);
        c.add(button);
          this.setSize(200,200);
        this.setLocation(100, 100);

      child1 = new MyChildWindow(this, 1);


           child2 = new MyChildWindow(this,2);


        child1.setVisible(true);
        child2.setVisible(true);
        this.setVisible(true);



    }
       public void setText(String s){
        txt.setText(s);
    }


    public void actionPerformed(ActionEvent e) {
            child1.setText(txt.getText());
          child2.setText(txt.getText());


    }

    public MyChildWindow getChild(int i){
        if(i==1)return child1;
        else return child2;
    }


    public static void main(String[] args) {
        new MyMainWindow();
    }


}

class MyChildWindow extends JFrame implements ActionListener {
    MyMainWindow parent;
       JTextField txt;
      JButton button;
    int num;

    public MyChildWindow(MyMainWindow parent, int num){
        super("child " + num );
        this.parent = parent;
        this.num = num;
           txt = new JTextField(10);
      button = new JButton("press");
           button.addActionListener(this);
        Container c = this.getContentPane();
          c.setLayout(new FlowLayout());
        c.add(txt);
        c.add(button);
        this.setSize(200,200);
        this.setLocation(num*200, num*200);

    }

    public void setText(String s){
        txt.setText(s);
    }

       public void actionPerformed(ActionEvent e) {
           parent.setText(txt.getText());
              MyChildWindow childNotMe = null;
           if(num == 1){
              childNotMe = parent.getChild(2);


           } else    childNotMe = parent.getChild(1);
           childNotMe.setText(txt.getText());



    }


}

Open in new window

Can a singleton extend AbstractTableModel?
I am being requested to create this singleton class.  I do understand that it is too hard to help with the merging classes because of runnable code.  But if someone could layout a simpleton class for me I will try and merge the code myself.  The class will need to extend AbstractTableModel.

Thanks
I think you can - this reposrts error when I try to instatiate directkly TestSingleton class

import javax.swing.table.AbstractTableModel;
import java.awt.*;

public class TestSingleton extends AbstractTableModel{

    private static TestSingleton instance = null;
      private TestSingleton() {
         // Exists only to defeat instantiation.
      }
      public static TestSingleton getInstance() {
         if(instance == null) {
            instance = new TestSingleton();
         }
         return instance;
      }


   public int getRowCount(){ return 1; }
  public int getColumnCount(){return 1;}
  public Object getValueAt(int row, int column){return null;}


}

class Try {
   

    public static void main(String[] args) {
      TestSingleton ts =   new TestSingleton();  // compilation error - cannot dierctly instantiatite
    }
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of for_yan
for_yan
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for that!

Before when I was passing in my tablemodel it was like this

//SelectedTModel myModel;
   SingletonSelectTable myModel;

//////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public EdiBaseDialog(Frame parent, TCSession theSession){
         super(parent, false);
         session = theSession;
         //myModel = new SelectedTModel();         
         myModel = SingletonSelectTable.getInstance();
         createDialog();  
   } //end Constructor

then is the method to draw the table

selectTable = new JTable(myModel);

I am not getting anything to the table - You can see the old way using SelectdTModel - that worked  what am I doing wrong?
THis is a static method now inside my new simpleton class

public static void add(TCComponentItemRevision tcRevision, TCComponentDataset selectedDataset){
               RevDataset pp = new RevDataset(tcRevision,selectedDataset);
         if(!rds.contains(pp))rds.add(pp);
}

How can I add a fireTableDataChanged?  

If I take away the static then i get a error on my ediHandler class

 SingletonSelectTable.add(tcRevision,selectedDataset);  Says that is a static call
Did you populate
SingletonSelectTable
with your model?

Post both old SelectdTModel  and new SingletonSelectTable

 SingletonSelectTable.add(tcRevision,selectedDataset);  - why would you do that?
It is a static call.
You need to create insance of SingletonSelectTable and use everywhere this iinstance
the same way you use instance myModel = new SelectedTModel();        
So you should say:
 myModel = SingletonSelectTable.getInstance(); <-- this is correct

and then

myModel.add(....)


The only thing changed is the way you obtain initially instance
of myModel - you dodn't cconstruct it with constructor but rather
cretd it with getInstance() - this is the only difference






I think you even don't need to change the name - just modify
this class like below (I marked after // added parts) and replace
  SelectedTModel  myModel = new SelectedTModel ()

with

 SelectedTModel  myModel =  SelectedTModel.getInstance();



public class SelectedTModel extends AbstractTableModel {
   
   private static final long serialVersionUID = 1L;
   private ArrayList<RevDataset> aList;
   
 private static SelectedTModel instance = null;  //add this varaible

   static final int ITEMID_COL   = 0;
   static final int REVID_COL    = 1;
   static final int PRL_COL      = 2;
   static final int DATASETNAME_COL  = 3;
   static final int DATASET_COL  = 4;
   
   static final int MAX_COLUMNS  = 5;

   public static SelectedTModel getInstance() {   //add thi method
         if(instance == null) {
            instance = new SelectedTModel ();
         }
         return instance;
      }

     
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   private SelectedTModel() { //change constructir to privatae
      super();
      this.aList = EdiSelection.rds;
      
      
                
   } // end constructor
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnCount()                         //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public int getColumnCount() {
	   
      return MAX_COLUMNS;
   } // end getColumnCount()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getRowCount()                            //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////   
   public int getRowCount() {
      return aList.size();
	  
   } // end getRowCount()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getValueAt()                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////  
   public Object getValueAt(int row, int column){
      Object o = null;
           
	  try {	   
	     switch (column) {
	        case ITEMID_COL: { o = aList.get(row).rev.getItem().getStringProperty("item_id"); break; }
	        case REVID_COL: { o = aList.get(row).rev.getStringProperty("item_revision_id"); break; }
	        case PRL_COL: { o = aList.get(row).rev.getRelatedComponent("IMAN_master_form_rev").getStringProperty("PRL"); break; }
	        case DATASETNAME_COL: { o = aList.get(row).componentdataset.getStringProperty("object_string"); break; }
	        case DATASET_COL: { o = aList.get(row).componentdataset.getStringProperty("object_type"); break; }
	     }
	  }
	  catch (Exception e) {
	     e.printStackTrace();
	 
      } 
	  return o;
   }
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnName()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public String getColumnName(int column) {
      String s = null;
      
      switch (column) {
      case ITEMID_COL:   { s = "ItemId"; break; }
      case REVID_COL:    { s = "RevId"; break; }
      case PRL_COL:      { s = "PRL"; break; }
      case DATASETNAME_COL:  { s = "Dataset Name"; break; }
      case DATASET_COL:  { s = "Dataset Type"; break; }
      }
   return s;
   } // end getColumnName()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                         removeSelectedRow()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public void removeSelectedRow(int row) {
	   boolean b = (aList.remove(row) != null);
	   //fireTableDataChanged();
	   fireTableRowsDeleted(row, row);
   } 
  
}//end SelectedTModel

Open in new window

Here is my singleton class - This class is creating my ArrayList as well as my TableModel.  I am no longer using EdiSelection class or SelectedTModel class

 
public class SingletonSelectTable extends AbstractTableModel{

   static final int ITEMID_COL   = 0;
   static final int REVID_COL    = 1;
   static final int PRL_COL      = 2;
   static final int DATASETNAME_COL  = 3;
   static final int DATASET_COL  = 4;
   static final int MAX_COLUMNS  = 5;
   
   static ArrayList<RevDataset> rds = new ArrayList<RevDataset>();
   
   private static SingletonSelectTable instance = null;
    
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////  
   private SingletonSelectTable() {       
   
   }
  
   public static SingletonSelectTable getInstance() {
      if(instance == null) {
         instance = new SingletonSelectTable();
      }
      return instance;
   }

   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getRowCount()                            //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////   
   public int getRowCount() {
      return rds.size();
	   
   } // end getRowCount()
  
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnCount()                         //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public int getColumnCount() {
      return MAX_COLUMNS;
   } // end getColumnCount()
  
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getValueAt()                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////  
   public Object getValueAt(int row, int column){
      Object o = null;
           
	  try {	   
	     switch (column) {
	        case ITEMID_COL: { o = rds.get(row).rev.getItem().getStringProperty("item_id"); break; }
	        case REVID_COL: { o = rds.get(row).rev.getStringProperty("item_revision_id"); break; }
	        case PRL_COL: { o = rds.get(row).rev.getRelatedComponent("IMAN_master_form_rev").getStringProperty("PRL"); break; }
	        case DATASETNAME_COL: { o = rds.get(row).componentdataset.getStringProperty("object_string"); break; }
	        case DATASET_COL: { o = rds.get(row).componentdataset.getStringProperty("object_type"); break; }
	     }	    	     
	  }
	  catch (Exception e) {
	     e.printStackTrace();
	 
      } 
	  return o;
   }
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                             getColumnName()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public String getColumnName(int column) {
      String s = null;
      
      switch (column) {
      case ITEMID_COL:   { s = "ItemId"; break; }
      case REVID_COL:    { s = "RevId"; break; }
      case PRL_COL:      { s = "PRL"; break; }
      case DATASETNAME_COL:  { s = "Dataset Name"; break; }
      case DATASET_COL:  { s = "Dataset Type"; break; }
      }
   return s;
   } // end getColumnName()
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                         removeSelectedRow()                          //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public void removeSelectedRow(int row) {
	   boolean b = (rds.remove(row) != null);
	   fireTableRowsDeleted(row, row);
	   
   }
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                            add()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public static void add(TCComponentItemRevision tcRevision, TCComponentDataset selectedDataset){
      //rds.add(new RevDataset(tcRevision,selectedDataset));
	  //System.out.println("Key :" + tcRevision.toString() + " Value : " + selectedDataset.toString() + " \n");
	   RevDataset pp = new RevDataset(tcRevision,selectedDataset);
	   if(!rds.contains(pp))rds.add(pp); 
	   //fireTableDataChanged();
   }// end add

   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                        getList()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////	
   public static ArrayList<RevDataset> getList(){
      return rds;	
   }// end GetList
   
   //////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                        clearList()                                     //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////	
   public static ArrayList<RevDataset> clearList(){
      rds.clear();
	  return rds;	
   }// end GetList

}//end SingletonSelectTable

Open in new window


This is the EdiHandler Class - This class is where I am calling the add function from the singleton Class

public class EdiHandler extends AbstractHandler {
   
	
   /* The constructor */
   public EdiHandler() {
   //EdiSelection es = new EdiSelection();
    SingletonSelectTable sst =  SingletonSelectTable.getInstance();
   }
  

Open in new window


this is how I am trying to call the add

  SingletonSelectTable.add(tcRevision,selectedDataset);

Open in new window


Does this look right?  Do I still need to create that instance in the constructor?

In my EdiBaseDialog Class I an doing it different

public class EdiBaseDialog extends AbstractAIFDialog {

SingletonSelectTable myModel;
   
//////////////////////////////////////////////////////////////////////////
   //                                                                      //
   //                              Constructor                             //
   //                                                                      //
   //////////////////////////////////////////////////////////////////////////
   public EdiBaseDialog(Frame parent, TCSession theSession){
	   super(parent, false);
	   session = theSession;
	   //myModel = new SelectedTModel();	   
	   myModel = SingletonSelectTable.getInstance();
	   createDialog();  
   } //end Constructor

Open in new window


Which way it correct - if any?
I think I everything working but really need a answer to the above question.  What is the correct place to create the instance of the Singleton Class

Right Now I am doing it like the EdiBaseDialog from above

This is OK place to create instance - just where you where doing it through constructor (with new)  before
 myModel = SingletonSelectTable.getInstance();


And the advantage of singleton is that you can call the same getInstance in another place like EdiHandler
and may be sure that you get the reference to the same instance.