A second reason not separating the business logic from the UI is that it makes it hard to move to another UI platform. For example, by separating the business logic, it is easily develop to use a Swing GUI, a console application, or an Android application without rewriting more portions of the framework than necessary.
For this tutorial, the case study will be a simple Student enrollment program. I have included the data classes to be used in the business logic, including the Course, Student, Teacher, and School classes. Since this is a tutorial about using a StateManager, I am simply going to attach these classes for reference. They are very simple and straight-forward.
The Student class models a simple Student, with a name, id, and a list of Courses.
Spoiler
The Course class includes the name, id, and department for the given Course. It also contains the roster of Students enrolled in this Course.
Spoiler
The Teacher class is pretty straight-forward, simply encapsulating a name and list of Courses.
Spoiler
And finally, the School class encapsulates the Students, Teachers, and Courses.
Spoiler
Now that the basic framework has been designed, let's work on defining the requirements of the application to help when designing the StateManager. This application will allow the user to:
-Add or Remove Teachers, Students, and Courses
-Update Students
-Enroll Students in Courses
-Assign Courses to Teachers
The next step is to design the StateManager. This class serves as the intermediary between the data and business logic, and the user interface, providing one clear point of access to the state and data. The StateManager will be a singleton class, making it a global for the application.
package statemanager.school;
import java.util.*;
/**
* @author Michael Levet
* @date 12/23/2011
*
* This class facilitates the interactions
* between the user interface and the application's
* logic and state.
***/
public class StateManager {
private static final StateManager manager = new StateManager();
private School school;
private StateManager() {
school = new School();
}
public static StateManager getInstance() {
return manager;
}
public void addTeacher(String name) {
school.addTeacher(new Teacher(name));
}
public Teacher getTeacher(String name) {
return school.getTeacher(name);
}
public Teacher getTeacher(int index) {
return school.getTeacher(index);
}
public void removeTeacher(String name) {
school.removeTeacher(school.getTeacher(name));
}
public List<Teacher> getAllTeachers() {
return new ArrayList<Teacher>(school.getAllTeachers());
}
public void addStudent(String name, String id) {
school.addStudent(new Student(name, id));
}
public Student getStudent(String name) {
return school.getStudent(name);
}
public Student getStudent(int index) {
return school.getStudent(index);
}
public void removeStudent(String name) {
school.removeStudent(school.getStudent(name));
}
public List<Student> getAllStudents() {
return new ArrayList<Student>(school.getAllStudents());
}
public void addCourse(String name, String id, String department) {
school.addCourse(new Course(name, id, department));
}
public Course getCourse(int index) {
return school.getCourse(index);
}
public Course getCourse(String name) {
return school.getCourse(name);
}
public void removeCourse(String name) {
school.removeCourse(school.getCourse(name));
}
public List<Course> getAllCourses() {
return new ArrayList<Course>(school.getAllCourses());
}
public void enrollStudent(String courseName, String studentName) {
Course c = school.getCourse(courseName);
Student s = school.getStudent(studentName);
c.addStudent(s);
}
public void dropStudentFromCourse(String courseName, String studentName) {
Course c = school.getCourse(courseName);
Student s = school.getStudent(studentName);
c.removeStudent(s);
}
public void addCourseForTeacher(String courseName, String teacherName) {
Course c = school.getCourse(courseName);
Teacher t = school.getTeacher(teacherName);
t.addCourse(c);
}
}
Now that the framework and StateManager have been created, let's take a look at how the StateManager should be used by a GUI component in the application. The basic idea is that the GUI components will use the appropriate getter and setter methods from the StateManager to populate and update the data for the application.
Let's start with the ManageStudentsFrame class. For the purpose of this tutorial, it is simplified to allow basic adding, editing, and deleting of Students. The Student names are listed by JRadioButtons, which are created by accessing the StateManager. Notice how the JRadioButtons have no actual ties to the Student objects- there is no Object association beyond setting the text of the JRadioButtons to the Student names.
package statemanager.gui;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import statemanager.school.*;
/**
* @author Michael Levet
* @date 12/25/2011
***/
public class ManageStudentsFrame extends JFrame implements ActionListener {
private JPanel mainPanel, buttonPane, optionsPane;
private JRadioButton[] buttons;
private JLabel title;
private StateManager manager;
private ButtonGroup group;
private JButton add, edit, delete;
public ManageStudentsFrame() {
super("Manage Students");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
manager = StateManager.getInstance();
mainPanel = new JPanel(new BorderLayout());
title = new JLabel("Select a Student to Edit or Delete It.");
title.setHorizontalAlignment(JLabel.CENTER);
mainPanel.add(title, BorderLayout.NORTH);
buttonPane = new JPanel(new GridLayout(0, 1));
mainPanel.add(buttonPane, BorderLayout.CENTER);
optionsPane = new JPanel();
add = new JButton("Add New Student");
edit = new JButton("Edit Student");
delete = new JButton("Delete");
add.addActionListener(this);
edit.addActionListener(this);
delete.addActionListener(this);
optionsPane.add(add);
optionsPane.add(edit);
optionsPane.add(delete);
mainPanel.add(optionsPane, BorderLayout.SOUTH);
this.add(mainPanel);
refreshStudents();
pack();
setLocationRelativeTo(null);
setVisible(true);
}
/**
* This method is used to repopulate the Students list
* as a group of JRadioButtons. It obtains the Students
* list from the StateManager.
***/
protected void refreshStudents() {
java.util.List<Student> students = manager.getAllStudents();
buttons = new JRadioButton[students.size()];
group = new ButtonGroup();
buttonPane.removeAll();
for (int i = 0; i < students.size(); i++) {
buttons[i] = new JRadioButton(students.get(i).getName());
group.add(buttons[i]);
buttonPane.add(buttons[i]);
}
pack();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == edit) {
Student s = manager.getStudent(getSelected().getText());
new EditStudentsDialog(s, this);
return;
} else if (e.getSource() == delete) {
Student s = manager.getStudent(getSelected().getText());
int option = JOptionPane.showConfirmDialog(this, "Delete Student",
"Are you sure you want to delete the Student "
+ s.getName(), JOptionPane.YES_NO_CANCEL_OPTION);
if (option == JOptionPane.YES_OPTION) {
manager.removeStudent(s.getName());
JOptionPane.showMessageDialog(null, "Student deleted");
}
this.refreshStudents();
return;
} else if (e.getSource() == add) {
new EditStudentsDialog(null, this);
}
}
private JRadioButton getSelected() {
for (JRadioButton b : this.buttons) {
if (b.isSelected()) {
return b;
}
}
return null;
}
public static void main(String[] args) {
new ManageStudentsFrame();
}
}
When the add and edit JButtons are pressed, a custom JDialog is displayed allowing the user to edit the name and text of the Student. While generally the GUI is not supposed to contain data elements, this JDialog does contain a Student as an instance field. This Student object is obtained from the StateManager in the ManageStudentsFrame actionPerformed() method. This is not a design concern, as the JDialog only updates what was obtained from the StateManager rather than continuing to move data across the GUI components.
package statemanager.gui;
import javax.swing.*;
import java.awt.event.*;
import statemanager.school.*;
/**
* @author Michael Levet
* @date 12/26/2011
***/
public class EditStudentsDialog extends JDialog implements ActionListener {
private Student student;
private JTextField name, id;
private JLabel nameLabel, idLabel;
private JButton submit;
private StateManager manager;
private ManageStudentsFrame parent;
public EditStudentsDialog(ManageStudentsFrame parent) {
this(null, parent);
}
public EditStudentsDialog(Student s, ManageStudentsFrame parent) {
this.student = s;
this.parent = parent;
this.setLocationRelativeTo(null);
this.setModalityType(ModalityType.APPLICATION_MODAL);
manager = StateManager.getInstance();
Box main = Box.createVerticalBox();
Box first = Box.createHorizontalBox();
nameLabel = new JLabel("Name:");
name = new JTextField(15);
first.add(nameLabel);
first.add(name);
Box second = Box.createHorizontalBox();
idLabel = new JLabel("ID:");
id = new JTextField(15);
second.add(idLabel);
second.add(id);
submit = new JButton("Submit");
submit.addActionListener(this);
main.add(first);
main.add(second);
main.add(submit);
this.add(main);
if (s != null) {
name.setText(s.getName());
id.setText(s.getId());
}
this.pack();
this.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
String newName = name.getText().trim();
String newId = id.getText().trim();
if (newName.length() == 0 || newId.length() == 0) {
JOptionPane.showMessageDialog(this, "You must enter both a name and id for the Student");
return;
}
if (student == null) {
manager.addStudent(newName, newId);
} //end if
else {
student.setName(newName);
student.setId(newId);
}
parent.refreshStudents();
this.setVisible(false);
this.dispose();
} //end actionPerformed()
}
Conclusion- While this tutorial did not cover designing the entire application, notice the important part- how the GUI used the StateManager to access the necessary data to populate its components, and to update the data as well.









MultiQuote





|