- 論壇徽章:
- 0
|
[這個(gè)貼子最后由eclipse在 2002/09/03 03:49pm 編輯]
設(shè)計(jì)模式:實(shí)戰(zhàn)MVC模式 turbochen(原作)
關(guān)鍵字 design pattern,mvc,observer,java
內(nèi)容:
1.MVC
2.Observer接口
3.模型Model
4.視圖View
5.控制器Controller
6.運(yùn)行程序
--------------------------------------------------------------------------------
MVC
“模型-視圖-控制器(Model-View-Controller,MVC)結(jié)構(gòu)是為那些需要為同樣的數(shù)據(jù)提供多個(gè)視圖的應(yīng)用程序而設(shè)計(jì)的,它很好的實(shí)現(xiàn)了數(shù)據(jù)層與表示層的分離。例如下圖中的例子:
![]()
我們看到,圖中的幾組數(shù)據(jù)以不同的形式(View)表現(xiàn)出來(lái),一個(gè)是表格樣式,一個(gè)是圖形樣式。
MVC把這種應(yīng)用程序分為三種對(duì)象類型:
模型:維護(hù)數(shù)據(jù)并提供數(shù)據(jù)訪問(wèn)方法。
視圖:給制模型的部分?jǐn)?shù)據(jù)或所有數(shù)據(jù)的可視圖。
控制器:處理事件.
以下是典型的MVC通信方式,
![]()
事件由控制器來(lái)處理,控制器接收用戶事件,并根據(jù)事件的類型來(lái)改變模型。
視圖事先會(huì)在模型中登記,當(dāng)模型數(shù)據(jù)發(fā)生改變時(shí),馬上通知已向此模型登記的每個(gè)視圖。
視圖從模取得最新的數(shù)據(jù)并刷新自己.
要實(shí)現(xiàn)MVC,最重要的一個(gè)環(huán)節(jié)是使用Design Pattern中的Observer模式。Observer模式允許某個(gè)對(duì)象在所觀察的對(duì)象發(fā)生修改時(shí)通知多個(gè)觀察者(Observer).
下面我們就以實(shí)例來(lái)講解如何用Obserer模式實(shí)現(xiàn)MVC的程序結(jié)構(gòu)。在我的例子中,我要實(shí)現(xiàn)一個(gè)學(xué)生年齡顯示的例子。分別用清單和圖形的方式顯示每個(gè)學(xué)生的年齡。當(dāng)年齡改變時(shí),自動(dòng)更新顯示。
Observer接口
為了實(shí)現(xiàn)觀察的對(duì)象發(fā)生修改時(shí)通知多個(gè)觀察者,通常要在被觀察者與觀察者之間有一個(gè)小的接口,如下:
/* file: Observer.java */
public interface Observer
{
public void dataUpdate(Model model);
}
這個(gè)接口中有一個(gè)dataUpdate(Model model)方法,只要實(shí)現(xiàn)了這個(gè)接口對(duì)象,就成了一個(gè)觀察者。
模型Model
再來(lái)建立一個(gè)數(shù)據(jù)模型。在我的例子中,先建立了一個(gè)數(shù)據(jù)對(duì)象:
/* file: Data.java */
public class Data
{
public int value; // 學(xué)生年齡值
public String name; // 學(xué)生名
}
現(xiàn)在來(lái)建立一個(gè)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,name);
}
}
public Model(Data[] data)
{
for ( int i = 0; i< data.length; i++ )
{
addData(data);
}
}
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();
}
// 用來(lái)向模型中登記觀察者.
public void registerObserver(Observer o)
{
observer.add(o);
}
public void removeObserver(Observer o)
{
observer.remove(o);
}
// 當(dāng)數(shù)據(jù)改變時(shí),由Controller調(diào)用此方法,通知各個(gè)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);
}
}
}
這個(gè)模型提供各種數(shù)據(jù)訪問(wèn)的方法。并提供一個(gè)changeModel(Model model)方法供Controller訪問(wèn)。還提供一個(gè)registerObserver(Observer o)方法,用來(lái)向Model中登記觀察者Observer。
視圖View
我們要實(shí)現(xiàn)一個(gè)清單顯示樣式的視圖View1和一個(gè)圖形方式顯示的視圖View2,并讓它們實(shí)現(xiàn)Observer接口,以便當(dāng)Model數(shù)據(jù)改變時(shí),自動(dòng)刷新自己.
/* 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;
}
}
// 當(dāng)模型數(shù)據(jù)發(fā)生改變時(shí),會(huì)自動(dòng)調(diào)用此方法來(lái)刷新圖形
public void dataUpdate(Model model)
{
/**@todo: Implement this Observer method*/
this.model = model;
repaint();
}
}
/* 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);
}
}
// 當(dāng)模型數(shù)據(jù)發(fā)生改變時(shí),會(huì)自動(dòng)調(diào)用此方法來(lái)刷新圖形
public void dataUpdate(Model model)
{
/**@todo: Implement this Observer method*/
this.model = model;
repaint();
}
}
控制器Controller
好了,MVC中的Model,Observer都建立好了,我們最后來(lái)做一個(gè)Controller:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
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, 20 );
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數(shù)據(jù)發(fā)生改變.
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);
}
}
運(yùn)行程序
各位可以將這些代碼各自存為相應(yīng)的源文件,執(zhí)行以下命令編譯
javac Controller.java
運(yùn)行
java Controller.class
就可以看到程序執(zhí)行的效果,
你可以試著改變兩個(gè)學(xué)生的年齡,按一下Update按鈕,相應(yīng)的視圖就更新了。怎么樣,體驗(yàn)到了MVC結(jié)構(gòu)給程序帶來(lái)的方便性了吧?;
|
|