In my program, there are two panels organized in CardLayout, panel 1 for user login, panel 2 for displaying user profile, and a singleton class is created for data sharing between these two panels.
Scenario can be, on panel 1, user type in username and password, click button "login", then in actionPerformed(ActionEvent e), username will be saved in singleton class String username = "somebody" and panel 2 will be made visible on the screen with the username in a JLabel.
My problem is, when I click the button, panel 2 shows up, but the username is lost, seems the change in actionPerformed() does not affect the singleton variable username. But if I put the change String username = "somebody" in constructor, the change takes place.
Any help will be appreciated!
Deliverance
March 17th, 2008, 12:42 AM
Well your Singleton class should only ever be instantiated once, then you can use that instance across many other classes. So assuming you have something like this as your singleton class, you should have no problem accessing the properties of that class:
Constructors in a singleton class should always be private. That way you are forcing the use of the static getInstance() method.
thinhare
March 17th, 2008, 08:22 AM
thanks for reply. I've tested different ways of singleton, didn't work out though. the original code was long and a little messy. below is the simplified version which maintains the problem.
This is the main class of the program.
import javax.swing.*;
import java.awt.*;
public class AlbumSys extends JFrame {
JPanel pnlCards = null;
CardLayout layout = null;
public AlbumSys ()
{
layout = new CardLayout();
pnlCards = new JPanel(layout);
pnlCards.add(new PanelAuthen(), "authentication");
pnlCards.add(new PanelAlbumList(), "albumlist");
getContentPane().add(pnlCards);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void init()
{
setLocation(500, 400);
pack();
setVisible(true);
while(true) {
if (Repository.stageChanged) {
switch (Repository.currStage) {
case 0:
layout.show(pnlCards, "authentication");
break;
case 1:
layout.show(pnlCards, "albumlist");
break;
default:
layout.show(pnlCards, "authentication");
}
Repository.stageChanged = false;
}
public class PanelAuthen extends JPanel implements ActionListener{
public PanelAuthen()
{
Button btnSignIn = new Button("signin");
btnSignIn.addActionListener(this);
add(btnSignIn);
}
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("signin")) {
Repository.username = "Haha! It's been changed!";
Repository.currStage = 1; // to switch to album list panel
Repository.stageChanged = true;
}
}
}
public PanelAlbumList()
{
JLabel lblTitle = new JLabel(Repository.username);
add(lblTitle);
}
}
ProgramThis
March 17th, 2008, 08:39 AM
First:
private final static Repository instance = new Repository();
Change this line to:
private static Repository instance;
And create a new instance of it in the getInstance() method if instance == null (just as Deliverance has already show you how to do...).
Secondly, just because the class is of the Singleton design pattern, and the values are static, that doesn't mean that you shouldn't hold a reference to the object. In order to be able to make that work though, you are going to have to add getter and setter methods (which you should always do anyway instead of directly accessing the data member). This also is something that Deliverance has show for you.
I suggest you take a good look at his version compared to yours and see what you are doing differently. Deliverance took the time to show you how to do it, try the advice.
thinhare
March 17th, 2008, 09:06 AM
hello to ProgramThis,
sorry I didn't make myself clear.
In fact, I tried the way that Deliverance advised first, it did not solve the problem. The reason I put the version that you did not like was because it was shorter and easier for eyes. (you know all setters and getters make the code much longer.)
The version Deliverance advised is put below, the problem stays...
This is the main class of the program.
import javax.swing.*;
import java.awt.*;
public class AlbumSys extends JFrame {
JPanel pnlCards = null;
CardLayout layout = null;
public AlbumSys ()
{
layout = new CardLayout();
pnlCards = new JPanel(layout);
pnlCards.add(new PanelAuthen(), "authentication");
pnlCards.add(new PanelAlbumList(), "albumlist");
getContentPane().add(pnlCards);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void init()
{
setLocation(500, 400);
pack();
setVisible(true);
while(true) {
if (Repository.getInstance().getStageChanged()) {
switch (Repository.getInstance().getCurrStage()) {
case 0:
layout.show(pnlCards, "authentication");
break;
case 1:
layout.show(pnlCards, "albumlist");
break;
default:
layout.show(pnlCards, "authentication");
}
Repository.getInstance().setStageChanged(false);
}
public class PanelAuthen extends JPanel implements ActionListener{
public PanelAuthen()
{
Button btnSignIn = new Button("signin");
btnSignIn.addActionListener(this);
add(btnSignIn);
}
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("signin")) {
Repository.getInstance().setUsername("Haha! It's been changed!");
Repository.getInstance().setCurrStage(1); // to switch to album list panel
Repository.getInstance().setStageChanged(true);
}
}
}
public PanelAlbumList()
{
JLabel lblTitle = new JLabel(Repository.getInstance().getUsername());
add(lblTitle);
}
}
dlorde
March 17th, 2008, 09:25 AM
First:
private final static Repository instance = new Repository();
Change this line to:
private static Repository instance;
And create a new instance of it in the getInstance() method if instance == null (just as Deliverance has already show you how to do...).No, that's not needed. Although it seems very common, this 'lazy' initialization isn't necessary in singletons - it's simpler and more robust to do it the original way - because static instances are constructed on first access, not at program load time, so it is equivalent to lazy initialization without the unnecessary extra check every time the instance is required. NALOPKT!
See The Java Language Spec: 12.4.1 When Initialization Occurs (http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44557) (although it's not really a beacon of clarity :rolleyes: ).
Awaken people's curiosity. It is enough to open minds, do not overload them. Put there just a spark...
A. France
thinhare
March 17th, 2008, 09:41 AM
Hello to Dlorde,
I've implemented it in both way, none of them solved the problem. So I suppose the sigleton might not be where the problem lies.
Would you mind taking a look at the below zipped code? thanks.
dlorde
March 17th, 2008, 01:00 PM
From what I can see you're not using Repository as a singleton at all, you're just using it to hold static data. Using static data simply as 'global' variables this way is usually a very bad idea.
Java is an OO language. Objects should collaborate by passing messages between themselves (calling methods), not sharing a common global data pool - unless you're trying to simulate a persistent data store.
For example, if you want to display different text depending on a stage or state indication, you should have a class that keeps track of the stage or state that you can ask if the stage has changed, and if so you can ask it what the new text is. There should be no need for the kind of switch statement you're using.
Good programmers know what's beautiful and bad ones don't...
D. Gelernter
ProgramThis
March 17th, 2008, 03:38 PM
I'm not an expert on the singleton design pattern, but I do believe that you need a reference to the object that is created. Try:
Repository res = Repository.getInstance();
And then using this res variable to set and get information in each of the classes.
thinhare
March 17th, 2008, 03:43 PM
For example, if you want to display different text depending on a stage or state indication, you should have a class that keeps track of the stage or state that you can ask if the stage has changed, and if so you can ask it what the new text is. There should be no need for the kind of switch statement you're using.
to dlorde,
yes, I am more used to procedure-oriented programming, that is why it looks not so OO. but I don't quite get the idea that you said about having a class to keep track of changes, how? could you show me a small example..
dlorde
March 18th, 2008, 06:14 AM
... I do believe that you need a reference to the object that is created. Try:
Repository res = Repository.getInstance();You don't need to use a separate reference, Repository.getInstance() gives you direct access to the Repository instance. The only reason to use a separate reference variable is to make the code look tidier where it is used a lot - and although this is probably a good enough reason, it won't make any difference to the code execution.
And as I suggested before, if the data is static, and the singleton pattern is irrelevant and redundant. By all means provide public accessors ('getters') for the static data, and make it private, but for static data, the singleton is a red-herring.
Oh, and another reason for not doing explicit lazy instantiation in a singleton occurred to me - it's not thread safe, and can't be made thread safe without synchronizing the whole getInstance() method, which is very inefficient as it applies for every call when it's only needed the first time. None of this applies to the combined declaration/instantiation technique.
We flew down weekly to meet with IBM, but they thought the way to measure software was the amount of code we wrote, when really the better the software, the fewer lines of code...
W. Gates
damien1977
March 18th, 2008, 08:01 AM
thinhare,
You need to update the label text in PanelAlbumList to reflect the change in username -
Not wishing to sound too critical or discouraging, but, I think the major issue here is that the design behind your code is not clear. For example, why would the username be a part of the repository object? I would be inclined to treat the username as part of the authentication process (and probably wrapped up in a User object), and use listeners and events to inform PanelAlbumList of user changes (rather than using the while(true) loop).
For example, here is some rough code to illustrate the idea -
public interface UserUpdateListener {
... snip ...
public void userChanged(UserEvent ue);
... snip ...
}
public class PanelAlbumList extends JPanel implements UserUpdateListener {
public PanelAlbumList() {
... snip ...
AuthenticationManager.getInstance().addUserUpdateListener(this);
}
... snip ...
public void userChanged(UserEvent ue) {
setTitle(ue.getUser().getName());
}
... snip ...
}
public class AuthenticationManager {
... snip ...
public void authenticateUser(User u) {
... snip ...
fireUserChanged(u);
}
... snip ...
// Observer pattern
List<UserUpdateListener> updateListeners = new ArrayList();
public void addUserUpdateListener(UserUpdateListener uul) {
updateListeners.add(uul);
}
This may well be over-engineered for what you need. :D
It is tempting with design patterns to look for problems for them to solve (rather than vice versa). I worked with plenty of coders who think everything is a factory one week, then everything is a singleton the next. :D Practise and experience will soon teach you how to use them appropriately.
thinhare
March 19th, 2008, 03:04 PM
Well, I have to admit that it is not really a OO design. and I am working on the redesign, trying to make it a more decent one.
your code's been very explanatory, some parts out of it still need more time for me to digest, though.
I am sure I will come up with new questions.
Thanks.
codeguru.com
Copyright WebMediaBrands Inc., All Rights Reserved.