Is this your first time here? SwingWiki is a Java Swing Developer community site with an big archive of Swing-related usenet groups and mailing lists, but also tips, tricks and articles and book reviews written by your colleagues from around the world. If you came here through a search engine and did not find what you were looking for, make sure to check the wiki table of contents.

Using a Factory

Conventional Practice

A common practice in Swing programs, and even in Sun’s tutorials, is to subclass components to use them. Here’s a typical Hello World example.

class Main
{
	public static void main(String[] args)
	{
		MyFrame frame=new MyFrame();
		frame.setVisible(true);
	}
}
 
class MyFrame extends JFrame
{
	MyFrame()
	{
		super("My Frame");
		setSize(400,400);
		setLayout(new FlowLayout());
		add(new JButton("Hello World"));
	}
}
Why It's Used

There are a number of reasons that programmers do this.

  1. All the code to set the JFrame up is in one place.
  2. The accesses to JFrame’s methods don’t need qualifying, i.e., you can do ‘setLayout’, rather than ‘frame.setLayout’.
  3. If you want to override any methods, you’ve already got an appropriate place to do it.
  4. Other programmers (and tutorial writers) do it.
Why It Shouldn't Be Used

There are also a number of reasons not to do this.

  1. It doesn’t scale well, i.e., reusing code seems awkward. There is no technical reason for this, but empirically, it seems to result in code duplication, especially in novice programmers.
  2. Subclassing adds complexity. If you’re not specialising the frame (overriding its methods), it’s simpler not to subclass. If you are specialising it, make sure you need to. Swing classes are designed in such a way that you don’t need to subclass them, i.e., they are components.
  3. It is possible to accidentally override a method. JFrame and its parent classes define a huge number of methods, so it’s possible that, unless you know all the methods, you will accidentally use the same name as a method from JFrame (or its parent classes), e.g., validate(), doLayout(), frameInit(), pack(), print(), repaint(). If you do, you might interfere with JFrame’s normal operation. As of Java 1.5, with the Override annotation, IDEs can flag accidental overrides.
  4. It is not very explicit. When you see a method call, e.g., ‘revalidate()’, and you don’t recognise the name, you’ll need to look at the current class to see where it is defined. When you see that it isn’t defined here, you need to look at the static imports, then at the superclass, then at its superclass, recursively, until you find the method definition. Of course, IDEs help with this.
  5. It is not normally written consistently. Most of the time, when you use a JButton, you just instantiate and use it. You don’t extend JButton. The same is true with JLabel, JTextField, etc., so it seems inconsistent to do it with JFrame.

A Simple Alternative

The first alternative is to create the JFrame in the main method:

class Main
{
	public static void main(String[] args)
	{
		JFrame frame=new JFrame("My Frame");
		frame.setSize(400,400);
		frame.setLayout(new FlowLayout());
		frame.add(new JButton("Hello World"));
		frame.setVisible(true);
	}
}

Using A Factory Method

You might not always want the frame to be initialised in the same place you use it, you might prefer a dedicated place for this:

class Main
{
	public static void main(String[] args)
	{
		JFrame frame=MainFrameUtility.createMainFrame();
		frame.setVisible(true);
	}
}
 
class MainFrameUtility
{
	public static JFrame createMainFrame()
	{
		JFrame frame=new JFrame("My Frame");
		frame.setSize(400,400);
		frame.setLayout(new FlowLayout());
		frame.add(new JButton("Hello World"));
		return frame;
	}
}

What To Do Instead Of Adding a Field To A JFrame

The next problem is when you want to access the button later for some reason. Here is the subclassing way of doing it:

class MainFrame extends JFrame
{
	private JButton button=new JButton("Hello World");
 
	public MainFrame()
	{
		super("My Frame");
		setSize(400,400);
		setLayout(new FlowLayout());
		add(button);
	}
 
	public JButton getButton()
	{
		return button;
	}
}

Here is a way of doing this using composition instead of inheritance:

class MainFrame
{
	private JFrame frame;
	private JButton button;
 
	public MainFrame(JFrame frame,JButton button)
	{
		this.frame=frame;
		this.button=button;
	}
 
	public JFrame getJFrame()
	{
		return frame;
	}
 
	public JButton getJButton()
	{
		return button;
	}
}
 
class MainFrameUtility
{
	public static JFrame createMainFrame()
	{
		JFrame frame=new JFrame("My Frame");
		frame.setSize(400,400);
		frame.setLayout(new FlowLayout());
		JButton button=new JButton("Hello World");
		frame.add(button);
		return new MainFrame(frame,button);
	}
}

The Final Product

A more optimal way of doing this follows, but the ideas are the same:

interface MainFrame
{
	JButton getButton();
	JFrame getFrame();
}
 
final class MainFrameUtility()
{
	private MainFrameUtility()
	{
		throw new UnsupportedOperationException();
	}
 
	public static MainFrame createMainFrame()
	{
		final JFrame frame=new JFrame("My Frame");
		frame.setSize(400,400);
		frame.setLayout(new FlowLayout());
		final JButton button=new JButton("Hello World");
		frame.add(button);
 
		return new MainFrame()
		{
			public JFrame getJFrame()
			{
				return frame;
			}
 
			public JButton getJButton()
			{
				return button;
			}
		};
	}
}

Things to do:

  • Demonstrate this with a subclassed JPanel with custom painting.
  • Consider making the MainFrame class and MainFrameUtility class one class.
 

Comments? Corrections? Contact us or Login to edit pages directly (registration is free and takes less than displaying a JLabel)
  best/using_a_factory.txt · Last modified: 2006/03/25 19:58 by 80.41.0.160 (ricky_clarkson)
 
Recent changes | RSS changes | Table of contents | News Archive | Terms And Conditions | Register | The Quest For Software++| Ruby Resources| Agile acceptance testing resources