Separating business logic
from UI logic
In general, you’ll want to design your classes so that each one does “only one thing.” This is particularly important when user-interface code is concerned, since it’s easy to tie up “what you’re doing” with “how you’re displaying it.” This kind of coupling prevents code reuse. It’s much more desirable to separate your “business logic” from the GUI. This way, not only can you reuse the business logic more easily, but it’s also easier to reuse the GUI.
Another issue is multitiered systems, where the “business objects” reside on a completely separate machine. This central location of the business rules allows changes to be instantly effective for all new transactions, and is thus a compelling way to set up a system. However, these business objects can be used in many different applications and so should not be tied to any particular mode of display. They should just perform the business operations and nothing more.[84]
The following example shows how easy it is to separate the business logic from the GUI code:
//: c14:Separation.java
// Separating GUI logic and business objects.
// <applet code=Separation width=250 height=150></applet>
import javax.swing.*;
import java.awt.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.applet.*;
import com.bruceeckel.swing.*;
class BusinessLogic {
private int modifier;
public BusinessLogic(int mod) { modifier = mod; }
public void setModifier(int mod) { modifier = mod; }
public int getModifier() { return modifier; }
// Some business operations:
public int calculation1(int arg){ return arg * modifier;}
public int calculation2(int arg){ return arg + modifier;}
}
public class Separation extends JApplet {
private JTextField
t = new JTextField(15),
mod = new JTextField(15);
private JButton
calc1 = new JButton("Calculation 1"),
calc2 = new JButton("Calculation 2");
private BusinessLogic bl = new BusinessLogic(2);
public static int getValue(JTextField tf) {
try {
return Integer.parseInt(tf.getText());
} catch(NumberFormatException e) {
return 0;
}
}
class Calc1L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText(Integer.toString(
bl.calculation1(getValue(t))));
}
}
class Calc2L implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.setText(Integer.toString(
bl.calculation2(getValue(t))));
}
}
// If you want something to happen whenever
// a JTextField changes, add this listener:
class ModL implements DocumentListener {
public void changedUpdate(DocumentEvent e) {}
public void insertUpdate(DocumentEvent e) {
bl.setModifier(getValue(mod));
}
public void removeUpdate(DocumentEvent e) {
bl.setModifier(getValue(mod));
}
}
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(t);
calc1.addActionListener(new Calc1L());
calc2.addActionListener(new Calc2L());
JPanel p1 = new JPanel();
p1.add(calc1);
p1.add(calc2);
cp.add(p1);
mod.getDocument().addDocumentListener(new ModL());
JPanel p2 = new JPanel();
p2.add(new JLabel("Modifier:"));
p2.add(mod);
cp.add(p2);
}
public static void main(String[] args) {
Console.run(new Separation(), 250, 100);
}
} ///:~
You can see that BusinessLogic is a straightforward class that performs its operations without even a hint that it might be used in a GUI environment. It just does its job.
Separation keeps track of all the UI details, and it talks to BusinessLogic only through its public interface. All the operations are centered around getting information back and forth through the UI and the BusinessLogic object. So Separation, in turn, just does its job. Since Separation knows only that it’s talking to a BusinessLogic object (that is, it isn’t highly coupled), it could be massaged into talking to other types of objects without much trouble.
Thinking in terms of separating UI from business logic also makes life easier when you’re adapting legacy code to work with Java.