Java/Design Pattern/MVC Pattern
MVC Implementation
<source lang="java">
/*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002. * All rights reserved. Software written by Ian F. Darwin and others. * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS"" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s, * pioneering role in inventing and promulgating (and standardizing) the Java * language and environment is gratefully acknowledged. * * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for * inventing predecessor languages C and C++ is also gratefully acknowledged. */
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import java.io.IOException; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**
* MVC Implementation * * @author Ian Darwin, http://www.darwinsys.ru/ * @version $Id: MVCDemo.java,v 1.3 2004/03/21 00:44:36 ian Exp $ */
public class MVCDemo {
/** "main program" method - construct and show */ public static void main(String[] av) { // Create the data model final Model model = new Model(); // create a JFrame to hold it all. final JFrame f = new JFrame("MVC"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container cp = f.getContentPane(); JPanel tp = new JPanel(); cp.add(tp, BorderLayout.NORTH); tp.add(new JLabel("New value:")); final JTextField tf = new JTextField(10); tp.add(tf); tf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { model.add(tf.getText()); tf.setText(""); } }); JPanel p = new JPanel(); // The first View is a JTextArea subclassed to have // an easy way of setting the data from a java.util.List final TextView tv = new TextView(); model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent evt) { Object o = evt.getSource(); Model m = (Model) o; tv.setListData(m.getData()); } }); tv.setBackground(Color.RED); tv.setSize(100, 100); p.add(tv); // The second View is the simplistic Grapher program from // the Java Cookbook "Graphics" chapter (../graphics/Grapher.java) final Grapher vv = new Grapher(); model.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent evt) { Object o = evt.getSource(); Model m = (Model) o; vv.setListData(m.getData()); } }); vv.setBackground(Color.YELLOW); vv.setSize(100, 100); p.add(vv); cp.add(p, BorderLayout.CENTER); f.pack(); f.setLocation(100, 100); f.setVisible(true); }
} class TextView extends JTextArea {
public TextView() { super(20, 20); } public void setListData(ArrayList l) { setText(""); for (int i = 0; i < l.size(); i++) { append((String) l.get(i)); append("\n"); } }
} class Model {
private ArrayList list = new ArrayList(); public void add(String s) { list.add(s); fireChange(); } public ArrayList getData() { return list; } // Sun recommends using javax.swing.EventListenerList, but this is easier ArrayList changeListeners = new ArrayList(); public void addChangeListener(ChangeListener cl) { changeListeners.add(cl); } public void removeChangeListener(ChangeListener cl) { changeListeners.remove(cl); } protected void fireChange() { ChangeEvent evt = new ChangeEvent(this); for (int i = 0; i < changeListeners.size(); i++) { ChangeListener cl = (ChangeListener) changeListeners.get(i); cl.stateChanged(evt); } }
} class Grapher extends JPanel {
/** Multiplier for range to allow room for a border */ public final static double BORDERFACTOR = 1.1f; /** The list of Point points. */ protected ArrayList data; /** The minimum and maximum X values */ protected double minx = Integer.MAX_VALUE, maxx = Integer.MIN_VALUE; /** The minimum and maximum Y values */ protected double miny = Integer.MAX_VALUE, maxy = Integer.MIN_VALUE; /** The range of X and Y values */ protected double xrange, yrange; public Grapher() { data = new ArrayList(); figure(); } /** * Set the list data from a list of Strings, where the x coordinate is * incremented automatically, and the y coordinate is made from the String * in the list. */ public void setListDataFromYStrings(ArrayList newData) { data.clear(); for (int i = 0; i < newData.size(); i++) { Point2D p = new Point2D.Double(); p.setLocation(i, java.lang.Double.parseDouble((String) newData .get(i))); data.add(p); } figure(); } /** Set the list from an existing List, as from GraphReader.read() */ public void setListData(ArrayList newData) { data = newData; figure(); } /** Compute new data when list changes */ private void figure() { // find min & max for (int i = 0; i < data.size(); i++) { Point2D d = (Point2D) data.get(i); if (d.getX() < minx) minx = d.getX(); if (d.getX() > maxx) maxx = d.getX(); if (d.getY() < miny) miny = d.getY(); if (d.getY() > maxy) maxy = d.getY(); } // Compute ranges xrange = (maxx - minx) * BORDERFACTOR; yrange = (maxy - miny) * BORDERFACTOR; } /** * Called when the window needs painting. Computes X and Y range, scales. */ public void paintComponent(Graphics g) { super.paintComponent(g); Dimension s = getSize(); if (data.size() < 2) { g.drawString("Insufficient data: " + data.size(), 10, 40); return; } // Compute scale factors double xfact = s.width / xrange; double yfact = s.height / yrange; // Scale and plot the data for (int i = 0; i < data.size(); i++) { Point2D d = (Point2D) data.get(i); double x = (d.getX() - minx) * xfact; double y = (d.getY() - miny) * yfact; // Draw a 5-pixel rectangle centered, so -2 both x and y. // AWT numbers Y from 0 down, so invert: g.drawRect(((int) x) - 2, s.height - 2 - (int) y, 5, 5); } } public Dimension getPreferredSize() { return new Dimension(150, 150); } public static void main(String[] args) throws IOException { final JFrame f = new JFrame("Grapher"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Grapher grapher = new Grapher(); f.setContentPane(grapher); f.setLocation(100, 100); f.pack(); ArrayList data = new ArrayList(); data.add("a"); grapher.setListData(data); f.setVisible(true); }
}
</source>
MVC Loop
<source lang="java">
/*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002. All rights * reserved. Software written by Ian F. Darwin and others. $Id: LICENSE,v 1.8 * 2004/02/09 03:33:38 ian Exp $ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. 2. Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS"" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee cup" * logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s, * pioneering role in inventing and promulgating (and standardizing) the Java * language and environment is gratefully acknowledged. * * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for * inventing predecessor languages C and C++ is also gratefully acknowledged. */
// Investigate if you can get into a loop with a Swing-based MVC app; // in particular, if you do setSelectedIndex on a choice, does it // generate a secondary event? Push the button and see! // If it does, you must be careful to avoid event propagation loops. // This example doesn"t show it, but you can loop with other implementations. // For example, if a changed() method updates the model"s data. import java.awt.Button; import java.awt.Container; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComboBox; import javax.swing.JFrame; public class MVCLoop {
public static void main(String[] a) { Button b; final JComboBox ch; JFrame f = new JFrame("Testing"); Container cp = f.getContentPane(); cp.setLayout(new FlowLayout()); b = new Button("Set"); ch = new JComboBox(); cp.add(b); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int cur = ch.getSelectedIndex(); int len = ch.getItemCount(); cur++; cur %= len; ch.setSelectedIndex(cur); } }); ch.addItem("En/ett"); ch.addItem("Zwei"); ch.addItem("Tres"); ch.addItem("Four"); ch.addItem("Funf"); ch.addItem("Seis"); ch.addItem("Siete"); ch.addItem("Octo"); cp.add(ch); ch.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(e); } }); f.pack(); f.setVisible(true); }
}
</source>
MVC Pattern in Java 2
<source lang="java">
//[C] 2002 Sun Microsystems, Inc.--- import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Iterator; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class RunMVCPattern {
public static void main(String [] arguments){ System.out.println("Example for the MVC pattern"); System.out.println(); System.out.println("In this example, a Contact is divided into"); System.out.println(" Model, View and Controller components."); System.out.println(); System.out.println("To illustrate the flexibility of MVC, the same"); System.out.println(" Model will be used to provide information"); System.out.println(" to two View components."); System.out.println(); System.out.println("One view, ContactEditView, will provide a Contact"); System.out.println(" editor window and will be paired with a controller"); System.out.println(" called ContactEditController."); System.out.println(); System.out.println("The other view, ContactDisplayView, will provide a"); System.out.println(" display window which will reflect the changes made"); System.out.println(" in the editor window. This view does not support"); System.out.println(" user interaction, and so does not provide a controller."); System.out.println(); System.out.println("Creating ContactModel"); ContactModel model = new ContactModel(); System.out.println("Creating ContactEditView and ContactEditController"); ContactEditView editorView = new ContactEditView(model); model.addContactView(editorView); createGui(editorView, "Contact Edit Window"); System.out.println("Creating ContactDisplayView"); ContactDisplayView displayView = new ContactDisplayView(); model.addContactView(displayView); createGui(displayView, "Contact Display Window"); } private static void createGui(JPanel contents, String title){ JFrame applicationFrame = new JFrame(title); applicationFrame.getContentPane().add(contents); applicationFrame.addWindowListener(new WindowCloseManager()); applicationFrame.pack(); applicationFrame.setVisible(true); } private static class WindowCloseManager extends WindowAdapter{ public void windowClosing(WindowEvent evt){ System.exit(0); } }
} //Model class Contact{
private String firstName; private String lastName; private String title; private String organization; private ContactView view; public Contact(ContactView v){ firstName = ""; lastName = ""; title = ""; organization = ""; view = v; } public String getFirstName(){ return firstName; } public String getLastName(){ return lastName; } public String getTitle(){ return title; } public String getOrganization(){ return organization; } public void setFirstName(String newFirstName){ firstName = newFirstName; } public void setLastName(String newLastName){ lastName = newLastName; } public void setTitle(String newTitle){ title = newTitle; } public void setOrganization(String newOrganization){ organization = newOrganization; } public void updateModel(String newFirstName, String newLastName, String newTitle, String newOrganization){ if ((newFirstName != null) && !newFirstName.equals("")){ setFirstName(newFirstName); } if ((newLastName != null) && !newLastName.equals("")){ setLastName(newLastName); } if ((newTitle != null) && !newTitle.equals("")){ setTitle(newTitle); } if ((newOrganization != null) && !newOrganization.equals("")){ setOrganization(newOrganization); } updateView(); } private void updateView(){ view.refreshContactView(firstName, lastName, title, organization); }
}
interface ContactView{
public void refreshContactView(String firstName, String lastName, String title, String organization);
} class ContactModel{
private String firstName; private String lastName; private String title; private String organization; private ArrayList contactViews = new ArrayList(); public ContactModel(){ this(null); } public ContactModel(ContactView view){ firstName = ""; lastName = ""; title = ""; organization = ""; if (view != null){ contactViews.add(view); } } public void addContactView(ContactView view){ if (!contactViews.contains(view)){ contactViews.add(view); } } public void removeContactView(ContactView view){ contactViews.remove(view); } public String getFirstName(){ return firstName; } public String getLastName(){ return lastName; } public String getTitle(){ return title; } public String getOrganization(){ return organization; } public void setFirstName(String newFirstName){ firstName = newFirstName; } public void setLastName(String newLastName){ lastName = newLastName; } public void setTitle(String newTitle){ title = newTitle; } public void setOrganization(String newOrganization){ organization = newOrganization; } public void updateModel(String newFirstName, String newLastName, String newTitle, String newOrganization){ if (!isEmptyString(newFirstName)){ setFirstName(newFirstName); } if (!isEmptyString(newLastName)){ setLastName(newLastName); } if (!isEmptyString(newTitle)){ setTitle(newTitle); } if (!isEmptyString(newOrganization)){ setOrganization(newOrganization); } updateView(); } private boolean isEmptyString(String input){ return ((input == null) || input.equals("")); } private void updateView(){ Iterator notifyViews = contactViews.iterator(); while (notifyViews.hasNext()){ ((ContactView)notifyViews.next()).refreshContactView(firstName, lastName, title, organization); } }
}
class ContactEditView extends JPanel implements ContactView{
private static final String UPDATE_BUTTON = "Update"; private static final String EXIT_BUTTON = "Exit"; private static final String CONTACT_FIRST_NAME = "First Name "; private static final String CONTACT_LAST_NAME = "Last Name "; private static final String CONTACT_TITLE = "Title "; private static final String CONTACT_ORG = "Organization "; private static final int FNAME_COL_WIDTH = 25; private static final int LNAME_COL_WIDTH = 40; private static final int TITLE_COL_WIDTH = 25; private static final int ORG_COL_WIDTH = 40; private ContactEditController controller; private JLabel firstNameLabel, lastNameLabel, titleLabel, organizationLabel; private JTextField firstName, lastName, title, organization; private JButton update, exit; public ContactEditView(ContactModel model){ controller = new ContactEditController(model, this); createGui(); } public ContactEditView(ContactModel model, ContactEditController newController){ controller = newController; createGui(); } public void createGui(){ update = new JButton(UPDATE_BUTTON); exit = new JButton(EXIT_BUTTON); firstNameLabel = new JLabel(CONTACT_FIRST_NAME); lastNameLabel = new JLabel(CONTACT_LAST_NAME); titleLabel = new JLabel(CONTACT_TITLE); organizationLabel = new JLabel(CONTACT_ORG); firstName = new JTextField(FNAME_COL_WIDTH); lastName = new JTextField(LNAME_COL_WIDTH); title = new JTextField(TITLE_COL_WIDTH); organization = new JTextField(ORG_COL_WIDTH); JPanel editPanel = new JPanel(); editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.X_AXIS)); JPanel labelPanel = new JPanel(); labelPanel.setLayout(new GridLayout(0, 1)); labelPanel.add(firstNameLabel); labelPanel.add(lastNameLabel); labelPanel.add(titleLabel); labelPanel.add(organizationLabel); editPanel.add(labelPanel); JPanel fieldPanel = new JPanel(); fieldPanel.setLayout(new GridLayout(0, 1)); fieldPanel.add(firstName); fieldPanel.add(lastName); fieldPanel.add(title); fieldPanel.add(organization); editPanel.add(fieldPanel); JPanel controlPanel = new JPanel(); controlPanel.add(update); controlPanel.add(exit); update.addActionListener(controller); exit.addActionListener(new ExitHandler()); setLayout(new BorderLayout()); add(editPanel, BorderLayout.CENTER); add(controlPanel, BorderLayout.SOUTH); } public Object getUpdateRef(){ return update; } public String getFirstName(){ return firstName.getText(); } public String getLastName(){ return lastName.getText(); } public String getTitle(){ return title.getText(); } public String getOrganization(){ return organization.getText(); } public void refreshContactView(String newFirstName, String newLastName, String newTitle, String newOrganization){ firstName.setText(newFirstName); lastName.setText(newLastName); title.setText(newTitle); organization.setText(newOrganization); } private class ExitHandler implements ActionListener{ public void actionPerformed(ActionEvent event){ System.exit(0); } }
} class ContactEditController implements ActionListener{
private ContactModel model; private ContactEditView view; public ContactEditController(ContactModel m, ContactEditView v){ model = m; view = v; } public void actionPerformed(ActionEvent evt){ Object source = evt.getSource(); if (source == view.getUpdateRef()){ updateModel(); } } private void updateModel(){ String firstName = null; String lastName = null; if (isAlphabetic(view.getFirstName())){ firstName = view.getFirstName(); } if (isAlphabetic(view.getLastName())){ lastName = view.getLastName(); } model.updateModel( firstName, lastName, view.getTitle(), view.getOrganization()); } private boolean isAlphabetic(String input){ char [] testChars = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}; for (int i = 0; i < testChars.length; i++){ if (input.indexOf(testChars[i]) != -1){ return false; } } return true; }
} class ContactDisplayView extends JPanel implements ContactView{
private JTextArea display; public ContactDisplayView(){ createGui(); } public void createGui(){ setLayout(new BorderLayout()); display = new JTextArea(10, 40); display.setEditable(false); JScrollPane scrollDisplay = new JScrollPane(display); this.add(scrollDisplay, BorderLayout.CENTER); } public void refreshContactView(String newFirstName, String newLastName, String newTitle, String newOrganization){ display.setText("UPDATED CONTACT:\nNEW VALUES:\n" + "\tName: " + newFirstName + " " + newLastName + "\n" + "\tTitle: " + newTitle + "\n" + "\tOrganization: " + newOrganization); }
}
</source>