building loosely coupled apps with Swing.
I have a question about building loosely coupled apps using Swing.
Say I have a very simple class called "Junk" that holds 3 private variables, and manipulates them in the usual way.
class Junk
{
private String name;
privateint number;
privatedouble cost;
public Junk()
{
this.name ="";
this.number = 0;
this.cost = 0.0;
}
public Junk(String name,int number,double cost)
{
this.name = name;
this.number = number;
this.cost = cost;
}
publicdouble getCost()
{
return cost;
}
publicvoid setCost(double cost)
{
this.cost = cost;
}
public String getName()
{
return name;
}
publicvoid setName(String name)
{
this.name = name;
}
publicint getNumber()
{
return number;
}
publicvoid setNumber(int number)
{
this.number = number;
}
publicdouble getTotalCost()
{
return cost * number;
}
@Override
public String toString()
{
return name +", " + String.valueOf(number) +", " + String.valueOf(cost);
}
}
Say I want to build a class that extends JPanel whose sole job is to display Junk objects. Is it possible to build such a class where it knows nothing (or at least very little) about the junk class?
I attempted it in what seems like a clunky way by building a wrapper class that goes around Junk, and simulates giving Junk some gui-like savy without corrupting the purity of Junk:
import java.util.Hashtable;
import javax.swing.JTextField;
class JunkWrapperimplements Fooable//I have created an interface which eases the interactions
//between this class and the GUI
{
private Junk myJunk;// this must be the "wrappee"
private Hashtable<String, String> fieldNamesHT;
publicstatic String[] fieldNames =
{
"Name:",
"Number:",
"Cost:",
"Total Cost"
};
public JunkWrapper()
{
myJunk =new Junk();
fieldNamesHT =new Hashtable<String, String>();
initFieldNamesHT();
}
public JunkWrapper(Junk myJunk)
{
this.myJunk = myJunk;
fieldNamesHT =new Hashtable<String, String>();
initFieldNamesHT();
}
// here I link the fieldNames array with the Junk fields via a hashtable
privatevoid initFieldNamesHT()
{
fieldNamesHT.put(fieldNames[0], myJunk.getName());
fieldNamesHT.put(fieldNames[1], String.valueOf(myJunk.getNumber()));
fieldNamesHT.put(fieldNames[2], String.valueOf(myJunk.getCost()));
fieldNamesHT.put(fieldNames[3], String.valueOf(myJunk.getTotalCost()));
}
public String[] getFieldNames()
{
return fieldNames;
}
public String getFieldText(String s)
{
initFieldNamesHT();
return fieldNamesHT.get(s);
}
publicvoid setFieldText(String fieldName, String fieldText)
{
// TODO: this is yet to be implemented
}
}
And here is my very small interface:
interface Fooable
{
public String[] getFieldNames();
public String getFieldText(String string);
publicvoid setFieldText(String fieldName, String fieldText);
}
And finally, a JunkPane class that displays Junk. But again trying to do so with limited knowledge of Junk:
class JunkPaneextends JPanel
{
privatestaticfinalint GAP = 5;
private JComponent parent;
private JPanel leftLabelPanel;
private JPanel rightDVDPanel;
private Fooable myJunk;// the object that I'm displaying
//to hold the Junk or Fooable field names
private String[] fieldNames;
//a hashtable to create textfields
private Hashtable<String, JTextField> fooFields;
public JunkPane(JComponent parent, Fooable myJunk)
{
super();
this.parent = parent;
this.myJunk = myJunk;
fooFields =new Hashtable<String, JTextField>();
fieldNames = myJunk.getFieldNames().clone();
for (int i = 0; i < fieldNames.length; i++)
{
fooFields.put(fieldNames[i],new JTextField(14));
}
BorderLayout myBorderLO =new BorderLayout();
myBorderLO.setHgap(GAP);
myBorderLO.setVgap(GAP);
setLayout(myBorderLO);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
add(createLeftLabelsPane(), BorderLayout.WEST);
add(createRightFieldsPane(), BorderLayout.EAST);
}
private JPanel createLeftLabelsPane()
{
JPanel leftPane =new JPanel();
GridLayout myGridLO =new GridLayout(0,1);
myGridLO.setHgap(GAP);
myGridLO.setVgap(GAP);
leftPane.setLayout(myGridLO);
for (int i = 0; i < fieldNames.length; i++)
{
JLabel tempLabel =new JLabel(fieldNames[i]);
tempLabel.setHorizontalAlignment(SwingConstants.RIGHT);
leftPane.add(tempLabel);
}
return leftPane;
}
private JPanel createRightFieldsPane()
{
JPanel rightPane =new JPanel();
GridLayout myGridLO =new GridLayout(0,1);
myGridLO.setHgap(GAP);
myGridLO.setVgap(GAP);
rightPane.setLayout(myGridLO);
for (int i = 0; i < fieldNames.length; i++)
{
rightPane.add(fooFields.get(fieldNames[i]));
}
return rightPane;
}
publicvoid setFooable(Fooable myFoo)
{
this.myJunk = myFoo;
displayFooable();
}
privatevoid displayFooable()
{
for (int i = 0; i < fieldNames.length; i++)
{
fooFields.get(fieldNames[i]).setText(myJunk.getFieldText(fieldNames[i]));
}
}
public Fooable getFooable()
{
for (int i = 0; i < fieldNames.length; i++)
{
myJunk.setFieldText(fieldNames[i], fooFields.get(fieldNames[i]).getText());
}
return myJunk;
}
}
So, a lot of junk here, but my question is, is there a better way? Some design pattern that I'm missing? Cuz I have to admit, this is pretty darn ugly code. I apologize for the vagueness of my request, but as I'm trying to Grok vague concepts, it is hard for me to couch them in precise words. All help is appreciated -- Dukes if you help me learn ( no easy feat). Thanks! /Pete.
Message was edited by:
petes1234
[11846 byte] By [
petes1234a] at [2007-11-27 6:56:54]

# 1
Here's some more code that goes w/ the previous code -- if any one wants a running version of the prog.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
// the jframe that will hold the JunkPane of interest
class JunkFrame extends JFrame
{
private JunkLst junkList; // here's a list of Junk
private JunkPane myJunkPane; // here's my junk panel
private static final int GAP = 5;
public JunkFrame(JunkLst junkList)
{
super("Junk Program");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.junkList = junkList; // update an array of Junk
JPanel mainPanel = new JPanel(new BorderLayout());
getContentPane().add(mainPanel);
//uses junkWrapper class to get list of strings to use in jfields
myJunkPane = new JunkPane(null, new JunkWrapper());
myJunkPane.setFooable(new JunkWrapper(junkList.first()));
mainPanel.add(myJunkPane, BorderLayout.CENTER);
mainPanel.add(buttonPane(), BorderLayout.SOUTH);
}
private JPanel buttonPane()
{
JPanel btnPane = new JPanel(new BorderLayout());
btnPane.add(topButtonPane(), BorderLayout.NORTH);
btnPane.add(bottomButtonPane(), BorderLayout.SOUTH);
return btnPane;
}
private JPanel topButtonPane()
{
JPanel btnPane = new JPanel();
JButton firstBtn = new JButton("First");
JButton prevBtn = new JButton("Previous");
JButton nextBtn = new JButton("Next");
JButton lastBtn = new JButton("Last");
btnPane.add(firstBtn);
btnPane.add(prevBtn);
btnPane.add(nextBtn);
btnPane.add(lastBtn);
firstBtn.addActionListener(new topRowBtnListener());
prevBtn.addActionListener(new topRowBtnListener());
nextBtn.addActionListener(new topRowBtnListener());
lastBtn.addActionListener(new topRowBtnListener());
return btnPane;
}
class topRowBtnListener implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
topRowBtnAction(ae);
}
}
public void topRowBtnAction(ActionEvent ae)
{
JButton pressedBtn = (JButton)ae.getSource();
if (ae.getActionCommand().equals("First"))
{
myJunkPane.setFooable(new JunkWrapper(junkList.first()));
}
else if (ae.getActionCommand().equals("Last"))
{
myJunkPane.setFooable(new JunkWrapper(junkList.last()));
}
else if (ae.getActionCommand().equals("Next"))
{
myJunkPane.setFooable(new JunkWrapper(junkList.next()));
}
else if (ae.getActionCommand().equals("Previous"))
{
myJunkPane.setFooable(new JunkWrapper(junkList.prev()));
}
}
// btns for adding, removing junk,....
private JPanel bottomButtonPane()
{
JPanel btnPane = new JPanel();
GridLayout myGridLO = new GridLayout(1,0);
myGridLO.setHgap(GAP);
myGridLO.setVgap(GAP);
btnPane.setLayout(myGridLO);
btnPane.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
JButton addBtn = new JButton("Add");
JButton removeBtn = new JButton("Remove");
btnPane.add(addBtn);
btnPane.add(removeBtn);
addBtn.addActionListener(new bottomRowBtnListener());
removeBtn.addActionListener(new bottomRowBtnListener());
return btnPane;
}
class bottomRowBtnListener implements ActionListener
{
public void actionPerformed(ActionEvent ae)
{
bottomRowBtnAction(ae);
}
}
public void bottomRowBtnAction(ActionEvent ae)
{
if (ae.getActionCommand().equals("Add"))
{
}
else if (ae.getActionCommand().equals("Remove"))
{
int result = JOptionPane.showConfirmDialog(null, "Are You Sure?");
if (result == JOptionPane.YES_OPTION)
{
junkList.remove(junkList.getCurrent());
}
}
}
private static void createAndShowGUI()
{
JunkLst myList = new JunkLst();
myList.add(new Junk("foo0", 1, 25.50));
myList.add(new Junk("foo1", 25, 12.20));
myList.add(new Junk("foo2", 50, 20.0));
myList.add(new Junk("foo3", 12, 15.50));
myList.add(new Junk("foo4", 40, 5.5));
myList.add(new Junk("foo5", 100, 30.0));
JunkFrame jf = new JunkFrame(myList);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
Here's code for JunkLst, the storage repositry for the junk:
Here's some more code that goes w/ the previous code -- if any one wants a running version of the prog.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
class JunkLst implements Iterable<Junk>
{
private List<Junk> junkList;
private int circularIterator = 0;
public JunkLst()
{
junkList = new ArrayList<Junk>();
}
public boolean add(Junk j)
{
return junkList.add(j);
}
public boolean remove(Junk j)
{
return junkList.remove(j);
}
public Junk getCurrent()
{
checkIfSize0();
return junkList.get(circularIterator);
}
public Junk first()
{
checkIfSize0();
circularIterator = 0;
return junkList.get(circularIterator);
}
public Junk last()
{
checkIfSize0();
circularIterator = junkList.size() - 1;
return junkList.get(circularIterator);
}
public Junk next()
{
checkIfSize0();
circularIterator++;
circularIterator %= junkList.size();
return junkList.get(circularIterator);
}
public Junk prev()
{
checkIfSize0();
circularIterator--;
if (circularIterator < 0)
{
circularIterator = junkList.size() - 1;
}
return junkList.get(circularIterator);
}
private void checkIfSize0()
{
if (junkList.size() == 0)
{
throw new NullPointerException();
}
}
public Junk get(int index)
{
if (index >= junkList.size() || index < 0)
{
throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + junkList.size());
}
return junkList.get(index);
}
public Iterator<Junk> iterator()
{
return junkList.iterator();
}
public int size()
{
return junkList.size();
}
}
Addendum: corrected the posting for JunkLst class
Message was edited by:
petes1234
# 2
I think in the Object class, you can get at a list of all the methods somehow, I kind of forget. You might be able to build an all encompassing JPanel to display the details of any class using that.
# 3
> I think in the Object class, you can get at a list of
> all the methods somehow, I kind of forget.
Do you mean using reflection? I'm self-taught (a work still very much in progress) and haven't hit that subject yet.
> You might
> be able to build an all encompassing JPanel to
> display the details of any class using that.
Thanks for the kind suggestions. If you have any or know of any *simple* code samples of this, I'd live to digest it. Again, thanks!
I've looked the net w/ google searches on -- java Swing "loose coupling" -- but nothing that I consider excellent yet.
Wife's gonna kill me, but I can see another text book coming.
# 4
No bites? I can understand. It's a lot of code to sift through. But perhaps you can answer some questions without sifting through code. Even just a nudge in the right direction by way of web-site or book recommendations are appreciated.
1. How do most folks generally go about separating their GUI from their logic? Is there even a general method or does it vary extremely from case to case?
2. How much does the GUI need to know?
3. If you have a simple class of 4 fields: 1 string field, 1 int field, 1 double field, and 1 double calculated result (not truly a field but it has a "getter"), is it possible to develop a jpanel that can display this info without knowing about the info, in fact not even knowing how many variables it needs to display? This is what I was attempting w/ all this code.
4. Is this even desirable?
There is no right nor wrong, this is certainly not a test or homework (my days of studenthood ended years ago). Just morbid curiosity.
# 5
public class JClassPanel extends JPanel {
Object myObject;
public static void main(String[] args){
JFrame frame = new JFrame("Test");
frame.setContentPane(new JClassPanel(new TestObj()));
frame.setPreferredSize(new Dimension(200,200));
frame.setSize(200,200);
frame.setVisible(true);
}
public JClassPanel() {
}
public JClassPanel(Object obj) {
myObject = obj;
Class c = myObject.getClass();
Field[] fields = c.getDeclaredFields();
Method[] methods = c.getMethods();
this.setLayout(new GridLayout(fields.length, 2));
for(int i = 0; i < fields.length; i++){
JLabel nameLabel = new JLabel(fields[i].getName());
JLabel valueLabel = null;
for(int j = 0; j < methods.length; j++){
try{
String fieldName = fields[i].getName();
fieldName = fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1, fieldName.length());
if(methods[j].getName().startsWith("get") ||
methods[j].getName().startsWith("is")){
if(methods[j].getName().endsWith(fieldName)){
Class returnType =
methods[j].getReturnType();
//Object instance = returnType.newInstance();
String returnString = returnType.toString();
System.out.println(returnString);
if(returnString.equals(String.class.toString())){
valueLabel = new
JLabel((String)methods[j]
.invoke(myObject, null));
}
else
if(returnString.equals("char")){
valueLabel = new
JLabel(((Character)methods[j]
.invoke(myObject, null)).toString());
}
if(returnString.equals("double")){
valueLabel = new JLabel(((Double)methods[j].invoke(myObject, null)).toString());
}
if(returnString.equals("long")){
valueLabel = new JLabel(((Long)methods[j].invoke(myObject, null)).toString());
}
if(returnString.equals("byte")){
valueLabel = new JLabel(((Byte)methods[j].invoke(myObject, null)).toString());
}
if(returnString.equals("boolean")){
valueLabel = new JLabel(((Boolean)methods[j].invoke(myObject, null)).toString());
}
if(returnString.equals("int")){
valueLabel = new JLabel(((Integer)methods[j].invoke(myObject, null)).toString());
}
}
}
}
catch(Exception e){
System.out.println(e.toString());
valueLabel = new JLabel ("Unavailable");
}
}
this.add(nameLabel);
this.add(valueLabel);
}
}
}
So this is my basic solution. It only takes primitives though, so you might have to put in some extra code to deal with Objects. Essentially, it just takes an object, gets an array of all the fields and array of all the methods. Then it loops through the fields and tries to find a getter for it. It gets the name of the field, puts it in a JLabel and gets the value returned by the getter and puts it in a JLabel. If the getter isn't of the format getVariableName or isVariableName it won't find it. P.S. sorry about the formatting.
Message was edited by:
RedUnderTheBed
# 6
................................> > Message was edited by: > RedUnderTheBedThanks, I will study it and get back to you./Pete
# 7
@RedUnderTheBed: Ah, so you are using reflection. I've been meaning to read up on this, so I guess now's the time.
# 8
this article might be of interest to you http://java.sun.com/developer/technicalArticles/javase/mvc/index.html
# 9
> this article might be of interest to you> > http://java.sun.com/developer/technicalArticles/javase> /mvc/index.htmlExcellent, excellent article! It's just what I'm looking for. I can't thank you enough./Pete