HelloWorld 善战者,求之于势,不责于人;故能择人而任势。

知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。物有本末,事有终始。知所先后,则近道矣。

  BlogJava :: 首页 ::  :: 联系 ::  :: 管理 ::
  167 随笔 :: 1 文章 :: 40 评论 :: 0 Trackbacks

实战MVC模式

MVC

“模型-视图-控制器(Model-View-Controller,MVC)结构是为那些需要为同样的数据提供多个视图的应用程序而设计的,它很好的实现了数据层与表示层的分离。例如下图中的例子:

 

MVC

我们看到,图中的几组数据以不同的形式(View)表现出来,一个是表格样式,一个是图形样式。

MVC通信方式

MVC把这种应用程序分为三种对象类型:

 

  • 模型:维护数据并提供数据访问方法。
  • 视图:给制模型的部分数据或所有数据的可视图。
  • 控制器:处理事件.

以下是典型的MVC通信方式,

[{MVC_1.gif][MVC的通信方式]]

  1. 事件由控制器来处理,控制器接收用户事件,并根据事件的类型来改变模型。
  2. 视图事先会在模型中登记,当模型数据发生改变时,马上通知已向此模型登记的每个视图。
  3. 视图从模取得最新的数据并刷新自己.

要实现MVC,最重要的一个环节是使用Design Pattern中的Observer模式。Observer模式允许某个对象在所观察的对象发生修改时通知多个观察者(Observer).

下面我们就以实例来讲解如何用Obserer模式实现MVC的程序结构。在我的例子中,我要实现一个学生年龄显示的例子。分别用清单和图形的方式显示每个学生的年龄。当年龄改变时,自动更新显示。

Observer接口

为了实现观察的对象发生修改时通知多个观察者,通常要在被观察者与观察者之间有一个小的接口,如下:
/* file: Observer.java */
public interface Observer {
public void dataUpdate(Model model);
}

这个接口中有一个 dataUpdate(Model model) 方法,只要实现了这个接口对象,就成了一个观察者。

模型Model

再来建立一个数据模型。在我的例子中,先建立了一个数据对象:

 

/* file: Data.java */
public class Data {
public int value; // 学生年龄值

public String name; // 学生名
}

现在来建立一个 Model :

/* file: Model.java */
import java.util.*;
public class Model {
ArrayList data = new ArrayList();
ArrayList observer = new ArrayList();
public Model() {
super();
}
public Model(int[] value, String[] name) {
for (int i = 0; i < value.length; i++) {
addData(value[i], name[i]);
}
}
public Model(Data[] data) {
for (int i = 0; i < data.length; i++) {
addData(data[i]);
}
}
public void addData(int value, String name) {
Data data = new Data();
data.value = value;
data.name = name;
this.data.add(data);
}
public void addData(Data data) {
this.data.add(data);
}
public Data getData(int idx) {
return (Data) (data.get(idx));
}
public int size() {
return data.size();
}
// 用来向模型中登记观察者.
    public void registerObserver(Observer o) {
observer.add(o);
}
public void removeObserver(Observer o) {
observer.remove(o);
}
// 当数据改变时,由Controller调用此方法,通知各个Observer,刷新视图.
    public void changeModel(Model model) {
data.clear();
for (int i = 0; i < model.size(); i++) {
this.addData(model.getData(i));
}
dataUpdate();
}
private void dataUpdate() {
for (Iterator i = observer.iterator(); i.hasNext();) {
Observer o = (Observer) (i.next());
o.dataUpdate(this);
}
}
}

这个模型提供各种数据访问的方法。并提供一个 changeModel(Model model) 方法供 Controller 访问。还提供一个 registerObserver(Observer o) 方法,用来向 Model 中登记观察者 Observer

视图View

我们要实现一个清单显示样式的视图 View1 和一个图形方式显示的视图 View2 ,并让它们实现 Observer 接口,以便当 Model 数据改变时,自动刷新自己.
/* file: View1.java */
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class View1 extends JPanel implements Observer {
Model model;
public View1() {
}
public View1(Model model) {
try {
this.model = model;
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setBackground(Color.white);
this.setBorder(new TitledBorder(BorderFactory.createLineBorder(
Color.black, 1), "View1"));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (model == null)
return;
int x = 20, y = 50;
int h = g.getFontMetrics().getHeight();
for (int i = 0; i < model.size(); i++) {
Data data = model.getData(i);
g.drawString(data.name, x, y);
y += h;
g.drawString(String.valueOf(data.value), x, y);
y += h;
}
}
// 当模型数据发生改变时,会自动调用此方法来刷新图形
    public void dataUpdate(Model model) {
/** @todo: Implement this Observer method */
this.model = model;
repaint();
}
}

View2.java

/* file: View2.java */
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class View2 extends JPanel implements Observer {
Model model;
public View2() {
}
public View2(Model model) {
try {
this.model = model;
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setBackground(Color.white);
this.setBorder(new TitledBorder(BorderFactory.createLineBorder(
Color.black, 1), "View1"));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (model == null)
return;
int x = 20, y = 50;
int h = g.getFontMetrics().getHeight();
int width = this.getWidth();
int height = this.getHeight();
int sy = height / model.size();
int sx = width / 2;
for (int i = 0; i < model.size(); i++) {
Data data = model.getData(i);
int value = data.value;
int dx = 3;
int r = 3;
Color c = new Color((int) (255 * Math.random()), (int) (255 * Math
.random()), (int) (255 * Math.random()));
int cx = sx;
int cy = y + i * sy;
for (int j = 0; j < value; j++) {
g.setColor(c);
g.drawOval(cx, cy, r, r);
r += dx;
}
g.drawString(data.name, 25, cy);
}
}
// 当模型数据发生改变时,会自动调用此方法来刷新图形
    public void dataUpdate(Model model) {
/** @todo: Implement this Observer method */
this.model = model;
repaint();
}
}

控制器Controller

好了,MVC中的 Model , Observer 都建立好了,我们最后来做一个 Controller :

 

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Controller extends JFrame {
Model model = new Model();
View1 view1 = new View1(model);
View2 view2 = new View2(model);
JScrollPane jScrollPane1 = new JScrollPane();
JButton jButton1 = new JButton();
JTextField jTextField1 = new JTextField();
JTextField jTextField2 = new JTextField();
JLabel jLabel1 = new JLabel();
JLabel jLabel2 = new JLabel();
JLabel jLabel3 = new JLabel();
public Controller() {
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
Data[] data = new Data[2];
data[0] = new Data();
data[1] = new Data();
data[0].name = "Ted";
data[0].value = 20;
data[1].name = "Joy";
data[1].value = 14;
model.addData(data[0]);
model.addData(data[1]);
// 注意下面两行:向模型中登记它的观察者View1和View2.
        model.registerObserver(view1);
model.registerObserver(view2);
this.getContentPane().setLayout(null);
jScrollPane1.setBounds(new Rectangle(0, 0, 3, 3));
jButton1.setBounds(new Rectangle(309, 259, 101, 27));
jButton1.setText("Update");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jTextField1.setText("20");
jTextField1.setBounds(new Rectangle(80, 254, 52, 30));
jTextField2.setText("14");
jTextField2.setBounds(new Rectangle(178, 255, 50, 31));
jLabel1.setText("Age:");
jLabel1.setBounds(new Rectangle(41, 226, 47, 23));
jLabel2.setText("Ted");
jLabel2.setBounds(new Rectangle(42, 252, 35, 33));
jLabel3.setText("Joy");
jLabel3.setBounds(new Rectangle(144, 255, 31, 31));
view1.setBounds(new Rectangle(7, 5, 225, 208));
view2.setBounds(new Rectangle(234, 4, 219, 209));
this.getContentPane().add(jScrollPane1, null);
this.getContentPane().add(jTextField2, null);
this.getContentPane().add(jTextField1, null);
this.getContentPane().add(jLabel2, null);
this.getContentPane().add(jLabel3, null);
this.getContentPane().add(jLabel1, null);
this.getContentPane().add(jButton1, null);
this.getContentPane().add(view1, null);
this.getContentPane().add(view2, null);
}
// 按下Update按钮,通知Model数据发生改变.
    void jButton1_actionPerformed(ActionEvent e) {
Data[] data = new Data[2];
data[0] = new Data();
data[1] = new Data();
data[0].name = jLabel1.getText();
data[0].value = Integer.parseInt(jTextField1.getText());
data[1].name = jLabel2.getText();
data[1].value = Integer.parseInt(jTextField2.getText());
Model m = new Model(data);
this.model.changeModel(m);
}
public static void main(String[] args) {
Controller c = new Controller();
c.setSize(475, 310);
c.setVisible(true);
}
}

运行程序

各位可以将这些代码各自存为相应的源文件,执行以下命令编译

javac Controller.java

运行

java Controller.class

就可以看到程序执行的效果,

执行结果示图

你可以试着改变两个学生的年龄,按一下 Update 按钮,相应的视图就更新了。怎么样,体验到了MVC结构给程序带来的方便性了吧?;)

大家如果运行程序有任何问题,或需编译好的程序,可与我联络。

关于作者

陈先波(turbochen)是赛微科技公司(www.qissoft.com)的Java开发员,曾为多个技术网站撰稿。有超过三年的Java开发经验,主要致力于Java技术在质量管理信息系统中的应用,请通过turbochen@163.com与作者联系。

</script>

posted on 2008-02-15 15:26 helloworld2008 阅读(179) 评论(0)  编辑  收藏 所属分类: java

只有注册用户登录后才能发表评论。


网站导航: