|
The bean that was created for the "divider"
bean project worked quite well, but depended on the caller to extract the
result when needed. However, JBuilder also offers what are called "bound"
properties. These are properties that automatically call on an event when their
value changes. This can be used to place control in the hands of the bean.
To accomplish this, we need to be able to push the result back to the user
interface; in this version we make a simple alteration to the data module to
allow it to keep a reference to the MainFrame. We also alter the main frame
so that it offers a member function that can be called with a string; the member
function will then handle displaying the result in the user interface.
This is not an ideal arrangement - another level of listener in the main frame
so that the data module could send the event up to the user interface would
be much better. But because the focus of this project is the bound property,
we'll save that for later.
Here's the new bean. Notice the new logic, generated when the properties were
created, to make them bound:
/**
* Title: A basic bean with bound properties<p>
* Description: Offers bound properties for run-time triggering.<p>
* Copyright: Copyright (c) Mark Cashman<p>
* Company: <p>
* @author Mark Cashman
* @version 1.0
*/
package bounddivisionbean;
import java.beans.*;
public class BoundDivider
{
private String input1;
private transient PropertyChangeSupport propertyChangeListeners
= new PropertyChangeSupport(this);
private String input2;
private String result;
public BoundDivider()
{
}
public void setInput1(String newInput1)
{
String oldInput1 = input1;
input1 = newInput1;
propertyChangeListeners.firePropertyChange("input1",
oldInput1, newInput1);
}
public String getInput1()
{
return input1;
}
public synchronized void removePropertyChangeListener(PropertyChangeListener
l)
{
propertyChangeListeners.removePropertyChangeListener(l);
}
public synchronized void addPropertyChangeListener(PropertyChangeListener
l)
{
propertyChangeListeners.addPropertyChangeListener(l);
}
public void setInput2(String newInput2)
{
String oldInput2 = input2;
input2 = newInput2;
propertyChangeListeners.firePropertyChange("input2",
oldInput2, newInput2);
}
public String getInput2()
{
return input2;
}
public void setResult(String newResult)
{
result = newResult;
}
public String getResult()
{
String outputValue = "";
try
{
outputValue = (String.valueOf(Double.parseDouble(input1)
/ Double.parseDouble(input2)));
return outputValue;
}
catch (Throwable exception)
{
return "";
};
}
}
The bean remains self-contained. The listening object still has to ask the
bean for the result. But the bean tells the listening object when to ask. Where
before the source of the data for the bean had to be the same object asking
for the result, or one called by it (because how else would it know there was
a change?), now any object can be notified of a change to the bean. Indeed,
in this case, the data module is notified by the bean, not the user interface.
Here we have the data module...
//Title: Basic Application
//Version: 1.0
//Copyright: Copyright (c) Mark Cashman
//Author: Mark Cashman
//Company:
//Description:Reproduces the similar C++ Builder example allowing "calculator-like"
functions, demonstrating exception handling, input and output.
//Title: Basic Application
//Version: 1.0
//Copyright: Copyright (c) Mark Cashman
//Author: Mark Cashman
//Company:
//Description:Reproduces the similar C++ Builder example allowing "calculator-like"
functions, demonstrating exception handling, input and output.
package basicboundbeanapplication;
import com.borland.dx.dataset.*;
import bounddivisionbean.*;
import java.beans.*;
public class NonVisual implements DataModule
{
private static NonVisual myDM;
BoundDivider boundDivider = new BoundDivider();
public MainFrame myMainFrame;
public NonVisual()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
};
}
private void jbInit() throws Exception
{
boundDivider.addPropertyChangeListener(new
java.beans.PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent
e)
{
boundDivider_propertyChange(e);
}
});
}
public static NonVisual getDataModule()
{
if (myDM == null)
{
myDM = new NonVisual();
}
return myDM;
}
void boundDivider_propertyChange(PropertyChangeEvent e)
{
myMainFrame.setResult(boundDivider.getResult());
}
}
The key element here is the boundDivider.addPropertyChangeListener
in jbInit(), and boundDivider_propertyChange (the event handler
for the the property change, shared by both input1 and input2. Of course none
of this would work without the internal reference to the main form that is established
in the data module and set from the main frame...
/**
* Title: Basic Bean Application<p>
* Description: Reproduces the similar C++ Builder example allowing
"calculator-like" functions, demonstrating exception handling, input and output,
using a bean.<p>
* Copyright: Copyright (c) Mark Cashman<p>
* Company: <p>
* @author Mark Cashman
* @version 1.0
*/
package basicboundbeanapplication;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.borland.jbcl.layout.*;
import java.beans.*;
import javax.swing.event.*;
public class MainFrame extends JFrame
{
JPanel contentPane;
VerticalFlowLayout contentVerticalFlowLayout = new VerticalFlowLayout();
Box outputBox;
JLabel outputLabel = new JLabel();
JLabel outputValue = new JLabel();
JTextField input2Field = new JTextField();
Box input2Box;
JLabel input2Label = new JLabel();
JTextField input1Field = new JTextField();
Box input1Box;
JLabel input1Label = new JLabel();
NonVisual nonVisual = new NonVisual();
//Construct the frame
public MainFrame()
{
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//Component initialization
private void jbInit() throws Exception
{
nonVisual.myMainFrame = this;
contentPane = (JPanel) this.getContentPane();
outputBox = Box.createHorizontalBox();
input2Box = Box.createHorizontalBox();
input1Box = Box.createHorizontalBox();
contentPane.setLayout(contentVerticalFlowLayout);
this.setResizable(false);
this.setSize(new Dimension(400, 300));
this.setTitle("Test Application");
contentPane.setMinimumSize(new Dimension(120,
69));
contentPane.setPreferredSize(new Dimension(120,
69));
contentPane.setToolTipText("");
outputLabel.setText("Output ");
input2Label.setText("Input
");
input1Label.setText("Input
");
input1Field.addCaretListener(new javax.swing.event.CaretListener()
{
public void caretUpdate(CaretEvent
e)
{
input1Field_caretUpdate(e);
}
});
input2Field.addCaretListener(new javax.swing.event.CaretListener()
{
public void caretUpdate(CaretEvent
e)
{
input2Field_caretUpdate(e);
}
});
contentPane.add(input1Box, null);
input1Box.add(input1Label, null);
input1Box.add(input1Field, null);
contentPane.add(input2Box, null);
input2Box.add(input2Label, null);
input2Box.add(input2Field, null);
contentPane.add(outputBox, null);
outputBox.add(outputLabel, null);
outputBox.add(outputValue, null);
}
//Overridden so we can exit when window is closed
protected void processWindowEvent(WindowEvent e)
{
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING)
{
System.exit(0);
}
}
void setResult(String theResult)
{
outputValue.setText(theResult);
}
void input1Field_caretUpdate(CaretEvent e)
{
nonVisual.boundDivider.setInput1(input1Field.getText());
}
void input2Field_caretUpdate(CaretEvent e)
{
nonVisual.boundDivider.setInput2(input2Field.getText());
}
}
Here, of course, jbInit() sets up the pointer to the main frame in the data
module and also offers the setResult member that is called by the data module.
In conclusion, bound properties can be a useful way to decouple a bean from
its clients. This decoupling makes it easier for a bean to act as a communication
medium - input from one object, output to another, with neither the input source
nor the output destination knowing about each other.
|