The Model/View Pattern IntroductionSuccessful development of large applications depends on the principle
of divide and conquer. A large complex task
is best accomplished by dividing it into simpler, cooperating subtasks. However, we do not take full advantage of this
precept if we mix code for controlling an application's interface with code for managing
an applications underlying data. Consequently,
computer professionals frequently break large programs into a model and a view. The view handles the interface and communicates
with a model that manages the application's data. This approach simplifies the task of writing complex applications and
increases their maintainability. It is common
for users to request changes to an applications interface, so it is advantageous to
make these changes without getting entangled in the intricacies of the model. Similarly, changes can be made to the model
without worrying about the interface. The
separation of model and view is also beneficial when an application requires several
windows. A separate class supports each
window, or view, and all views communicate with a common model. In addition, on large projects teams of programmers can work
independently on the view and the model, provided they agree ahead of time on the public
methods included in the model. Responsibilities of the model and viewIn general, when we separate an application into a model and view,
the responsibilities of the view are as follows:
The responsibilities of the model are as follows:
The division of labor between the model and the view has proven
itself useful in many different situations and is called a pattern. There are many other patterns in the realm of
object-oriented programming. Each describes
how a common programming situation can be handled by a collection of classes with
predefined roles communicating in a predefined manner. ExampleAs an example of the mode/view pattern, we present an application
that maintains an array of student data. There
are three classes:
Here is a snapshot of the interface:
The next table describes the function of each button.
Here are listings for the three classes StudentTestScoresView,
StudentTestScoresModel,
and
Student: import java.awt.*; import BreezyGUI.*; public class StudentTestScoresView extends GBFrame{ // Window objects --------------------------------------
Button addButton = addButton ("Add" ,2,4,1,1); Button insertButton = addButton ("Insert",3,4,1,1); Button modifyButton = addButton ("Modify",4,4,1,1); Button deleteButton = addButton ("Delete",5,4,1,1);
Label blankLine1 = addLabel ("" , 6,1,1,1); Button firstButton = addButton ("<<", 7,1,1,1); Button previousButton = addButton ("<", 7,2,1,1); Button nextButton = addButton (">", 7,3,1,1); Button lastButton = addButton (">>", 7,4,1,1); Label nameLabel = addLabel ("Name" ,1,1,1,1); Label test1Label = addLabel ("Test 1" ,2,1,1,1); Label test2Label = addLabel ("Test 2" ,3,1,1,1); Label test3Label = addLabel ("Test 3" ,4,1,1,1); Label averageLabel = addLabel ("Average" ,5,1,1,1);
TextField nameField = addTextField ("",1,2,2,1); IntegerField test1Field = addIntegerField (0 ,2,2,1,1); IntegerField test2Field = addIntegerField (0 ,3,2,1,1); IntegerField test3Field = addIntegerField (0 ,4,2,1,1); IntegerField averageField = addIntegerField (0 ,5,2,1,1);
Label blankLine2 = addLabel ("" ,8,1,1,1);
Label countLabel = addLabel ("Count" ,9,1,1,1);
IntegerField countField = addIntegerField (0 ,9,2,1,1);
Label indexLabel = addLabel ("Current Index" ,9,3,1,1);
IntegerField indexField = addIntegerField (-1 ,9,4,1,1);
MenuItem sortByNameMI = addMenuItem ("Sort","By Name"); MenuItem sortByAverageMI = addMenuItem ("Sort","By Average");
// Other instance variables ---------------------------------
StudentTestScoresModel model;
// Constructor----------------------------------------------- public StudentTestScoresView(){ setTitle ("Student Scores -- Version 3");
model = new StudentTestScoresModel(); averageField.setEditable (false); countField.setEditable (false); indexField.setEditable (false); displayCurrentStudent(); }
// buttonClicked method-------------------------------------- public void buttonClicked (Button buttonObj){ if (buttonObj == addButton){ Student stud = getDataOnScreen(); String str = model.addStudent (stud); if (str != null) messageBox (str); else displayCurrentStudent(); }
// insert, modify, and delete are left as an exercise
else if (buttonObj == firstButton){ model.moveToFirstStudent(); displayCurrentStudent(); } else if (buttonObj == previousButton); // left as an exercise
else if (buttonObj == nextButton){ model.moveToNextStudent(); displayCurrentStudent(); } else if (buttonObj == lastButton); // left as an exercise }
// menuSelected method--------------------------------------- public void menuItemSelected (MenuItem menuItemObj){
if (menuItemObj == sortByNameMI){ model.sortStudentsByName(); model.moveToFirstStudent(); displayCurrentStudent(); }
// Sorting by average left as an exercise }
// Private methods------------------------------------------- Student getDataOnScreen(){ String nm = nameField.getText().trim();
int[] tests = new int[Student.NUM_TESTS]; tests[0] = test1Field.getNumber(); tests[1] = test2Field.getNumber(); tests[2] = test3Field.getNumber();
Student stud = new Student (nm, tests); return stud; }
void displayCurrentStudent(){ Student stud = model.getCurrentStudent(); if (stud == null){ nameField.setText (""); test1Field.setNumber (0); test2Field.setNumber (0); test3Field.setNumber (0); averageField.setNumber (0); }else{ nameField.setText (stud.getName()); test1Field.setNumber (stud.getScore(1)); test2Field.setNumber (stud.getScore(2)); test3Field.setNumber (stud.getScore(3)); averageField.setNumber (stud.getAverage()); } countField.setNumber (model.getStudentCount()); indexField.setNumber (model.getIndexSelectedStudent()); } public static void main (String[] args){ Frame frm = new StudentTestScoresView(); frm.setSize (400, 250); frm.setVisible(true); } } public class StudentTestScoresModel extends Object{ // Instance variables ---------------------------------
Student[] students = new Student[10]; int indexSelectedStudent; int studentCount;
// Constructor----------------------------------------------- public StudentTestScoresModel(){ indexSelectedStudent = -1; studentCount = 0; }
public String addStudent (Student stud){ if (studentCount == students.length) return "SORRY: student array is full";
String str = stud.validateData(); if (str != null) return str;
students[studentCount] = stud; indexSelectedStudent = studentCount; studentCount++;
return null; }
public Student getCurrentStudent(){ if (indexSelectedStudent == -1) return null; else return students[indexSelectedStudent]; } public int getIndexSelectedStudent(){ return indexSelectedStudent; } public int getStudentCount(){ return studentCount; }
void moveToFirstStudent(){ if (studentCount == 0) indexSelectedStudent = -1; else indexSelectedStudent = 0; }
void moveToPreviousStudent(){ // Exercise }
void moveToNextStudent(){ if (studentCount == 0) indexSelectedStudent = -1; else indexSelectedStudent = Math.min (studentCount - 1, indexSelectedStudent + 1); }
void moveToLastStudent(){ // Exericse }
void sortStudentsByName(){ for (int i = 0; i < studentCount - 1; i++){ String namei = students[i].getName(); for (int j = i + 1; j < studentCount; j++){ String namej = students[j].getName(); if (namei.compareTo (namej) > 0){ Student temp = students[i]; students[i] = students[j]; students[j] = temp; } } } } } public class Student extends Object{ public final static int NUM_TESTS = 3; private final static int MIN_SCORE = 0; private final static int MAX_SCORE = 100; private String name; private int[] tests = new int[NUM_TESTS]; public Student(){ name = ""; for (int i = 0; i < NUM_TESTS; i++) tests[i] = 0; }
public Student(String nm, int[] t){ name = nm; for (int i = 0; i < NUM_TESTS; i++) tests[i] = t[i]; }
public Student(Student s){ name = s.name; for (int i = 0; i < NUM_TESTS; i++) tests[i] = s.tests[i]; }
public void setName (String nm){ name = nm; }
public String getName (){ return name; }
public void setScore (int i, int score){ // where 1 <= i <= NUM_TESTS
tests[i - 1] = score; } public int getScore (int i){ // where 1 <= i <= NUM_TESTS
return tests[i - 1]; }
public int getAverage(){ int sum = 0; for (int i = 0; i < NUM_TESTS; i++) sum += tests[i]; return sum / NUM_TESTS; }
public int getHighScore(){ int highScore; highScore = tests[0]; for (int i = 1; i < NUM_TESTS; i++){ highScore = Math.max (highScore, tests[i]); } return highScore; }
public String toString(){ String str; str = "Name: " + name + "\n"; for (int i = 0; i < NUM_TESTS; i++){ str += "tests " + i + ": " + tests[i] + "\n"; } str += "Average: " + getAverage(); return str; }
public String validateData(){ if (name.equals ("")) return "SORRY: name required"; for (int i = 0; i < NUM_TESTS; i++){ if (tests[i] < MIN_SCORE || tests[i] > MAX_SCORE){ String str = "SORRY: must have "+ MIN_SCORE + " <= test score <= " + MAX_SCORE; return str; } } return null; } } |
| ||||