1.M-V-C ——Model--View--Controller,模式-视图-控制器,这是一种范型。模型对象正是应用系统存在的理由,你设计的对象,包含了数据、逻辑和其他在你的应用领域创建定制的类。视图通常是控件,用来显示和编辑,控制器位于二者中间,负责将每个改变的状态送进送出。而学习设计模式是理解MVC的钥匙。书中用一个iTunes的例子直观描述了MVC:
2.MVC的基本原理:
-
视图:用来呈现模型。视图通常直接从模型中取得它需要显示的数据。 视图不会直接操作模型。
-
控制器:取得用户的输入并解读其对模型的意思。 控制器不会实现应用逻辑,它为视图实现行为,将视图传过来的行为转化为模型上的动作。它只负责决定调用哪一个模型。
-
模型:持有所有的数据,状态和程序逻辑。模型没有注意到视图和控制器,虽然它提供了操纵和检索状态的接口,并且发送状态改变通知观察者。 模型只知道有一些观察者它需要通知,并且提供一些接口供视图和控制器获得并设置状态。
他们三者的交互如下图:
这里充分体现了我们“单一职责”的这个原则。
3.MVC模式分析:
1)视图和控制器实现了经典的策略模式:视图是一个对象,可以被调整使用不同的策略。视图只关心显示,而其行为的控制则都使用控制器进行。这样一来,视图和模型之间也完成了解耦,因为控制器负责和模型进行用户请求的交互。
2)视图中的显示中包含了很多的要素,这就用到了组合模式,当控制器告诉视图更新时,只需告诉视图最顶层的组件即可,组合会处理其余的事。
3)模型则实现了观察者模式,当状态改变时,相关对象将持续更新。
4.MVC实例——DJ View
这是一个控制节拍(BPM,每分钟XX拍)并产生鼓声的工具。下边是这个系统的核心,他负责根据节拍(可以设置可以读取)产生鼓声——模型:
我们先看看模型的接口:
public interface BeatModelInterface {
void initialize();
void on();
void off();
void setBPM(int bpm);
int getBPM();
void registerObserver(BeatObserver o);//有两种观察者,一种观察者希望每个节拍都被通知,另一种观察者希望BPM改变时被通知
void removeObserver(BeatObserver o);
void registerObserver(BPMObserver o);
void removeObserver(BPMObserver o);
}
根据这个接口,我们可以实现模型:
public class BeatModel implements BeatModelInterface, MetaEventListener {
Sequencer sequencer;
ArrayList beatObservers = new ArrayList();
ArrayList bpmObservers = new ArrayList();
int bpm = 90;
Sequence sequence;
Track track;
public void initialize() {
setUpMidi();
buildTrackAndStart();
}
public void on() {
sequencer.start();
setBPM(90);
}
public void off() {
setBPM(0);
sequencer.stop();
}
public void setBPM(int bpm) {
this.bpm = bpm;
sequencer.setTempoInBPM(getBPM());
notifyBPMObservers();
}
public int getBPM() {
return bpm;
}
void beatEvent() {
notifyBeatObservers();
}
public void registerObserver(BeatObserver o) {
beatObservers.add(o);
}
public void notifyBeatObservers() {
for(int i = 0; i< beatObservers.size(); i++) {
BeatObserver observer = (BeatObserver)beatObservers.get(i);
observer.updateBeat();
}
}
public void registerObserver(BPMObserver o) {
bpmObservers.add(o);
}
public void notifyBPMObservers() {
for(int i = 0; i< bpmObservers.size(); i++) {
BPMObserver observer = (BPMObserver)bpmObservers.get(i);
observer.updateBPM();
}
}
public void removeObserver(BeatObserver o) {
int i = beatObservers.indexOf(o);
if (i >= 0) {
beatObservers.remove(i);
}
}
public void removeObserver(BPMObserver o) {
int i = bpmObservers.indexOf(o);
if (i >= 0) {
bpmObservers.remove(i);
}
}
public void meta(MetaMessage message) {
if (message.getType() == 47) {
beatEvent();
sequencer.start();
setBPM(getBPM());
}
}
public void setUpMidi() {
try {
sequencer = MidiSystem.getSequencer();
sequencer.open();
sequencer.addMetaEventListener(this);
sequence = new Sequence(Sequence.PPQ,4);
track = sequence.createTrack();
sequencer.setTempoInBPM(getBPM());
} catch(Exception e) {
e.printStackTrace();
}
}
public void buildTrackAndStart() {
int[] trackList = {35, 0, 46, 0};
sequence.deleteTrack(null);
track = sequence.createTrack();
makeTracks(trackList);
track.add(makeEvent(192,9,1,0,4));
try {
sequencer.setSequence(sequence);
} catch(Exception e) {
e.printStackTrace();
}
}
public void makeTracks(int[] list) {
for (int i = 0; i< list.length; i++) {
int key = list[i];
if (key != 0) {
track.add(makeEvent(144,9,key, 100, i));
track.add(makeEvent(128,9,key, 100, i+1));
}
}
}
public MidiEvent makeEvent(int comd, int chan, int one, int two, int tick) {
MidiEvent event = null;
try {
ShortMessage a = new ShortMessage();
a.setMessage(comd, chan, one, two);
event = new MidiEvent(a, tick);
} catch(Exception e) {
e.printStackTrace();
}
return event;
}
}
我们现在要把视图挂上去,让这个模型可视化!BeatModel对视图一无所知,我们利用观察者模式当状态改变时,只要是注册为观察者的视图都会收到通知。而视图使用模型的API访问状态。
public class DJView implements ActionListener,BeatObserver, BPMObserver{//同时关心时时节拍和BPM的改变
BeatModelInterface model;
ControllerInterface controller;//视图持有模型和控制器的引用
JFrame viewFrame;
JPanel viewPanel;
BeatBar beatBar;
JLabel bpmOutputLabel;
JFrame controlFrame;
JPanel controlPanel;
JLabel bpmLabel;
JTextField bpmTextField;
JButton setBPMButton;
JButton increaseBPMButton;
JButton decreaseBPMButton;
JMenuBar menuBar;
JMenu menu;
JMenuItem startMenuItem;
JMenuItem stopMenuItem;
public DJView(ControllerInterface controller, BeatModelInterface model) {
this.controller = controller;
this.model = model;
model.registerObserver((BeatObserver)this);//注册观察者
model.registerObserver((BPMObserver)this);
}
public void createView() {
// Create all Swing components here
viewPanel = new JPanel(new GridLayout(1, 2));
viewFrame = new JFrame("View");
viewFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
viewFrame.setSize(new Dimension(100, 80));
bpmOutputLabel = new JLabel("offline", SwingConstants.CENTER);
beatBar = new BeatBar();
beatBar.setValue(0);
JPanel bpmPanel = new JPanel(new GridLayout(2, 1));
bpmPanel.add(beatBar);
bpmPanel.add(bpmOutputLabel);
viewPanel.add(bpmPanel);
viewFrame.getContentPane().add(viewPanel, BorderLayout.CENTER);
viewFrame.pack();
viewFrame.setVisible(true);
}
public void createControls() {
// Create all Swing components here
JFrame.setDefaultLookAndFeelDecorated(true);
controlFrame = new JFrame("Control");
controlFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
controlFrame.setSize(new Dimension(100, 80));
controlPanel = new JPanel(new GridLayout(1, 2));
menuBar = new JMenuBar();
menu = new JMenu("DJ Control");
startMenuItem = new JMenuItem("Start");
menu.add(startMenuItem);
startMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
controller.start();//视图的点击触发控制器的事件
}
});
stopMenuItem = new JMenuItem("Stop");
menu.add(stopMenuItem);
stopMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
controller.stop();
}
});
JMenuItem exit = new JMenuItem("Quit");
exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
menu.add(exit);
menuBar.add(menu);
controlFrame.setJMenuBar(menuBar);
bpmTextField = new JTextField(2);
bpmLabel = new JLabel("Enter BPM:", SwingConstants.RIGHT);
setBPMButton = new JButton("Set");
setBPMButton.setSize(new Dimension(10,40));
increaseBPMButton = new JButton(">>");
decreaseBPMButton = new JButton("<<");
setBPMButton.addActionListener(this);
increaseBPMButton.addActionListener(this);
decreaseBPMButton.addActionListener(this);
JPanel buttonPanel = new JPanel(new GridLayout(1, 2));
buttonPanel.add(decreaseBPMButton);
buttonPanel.add(increaseBPMButton);
JPanel enterPanel = new JPanel(new GridLayout(1, 2));
enterPanel.add(bpmLabel);
enterPanel.add(bpmTextField);
JPanel insideControlPanel = new JPanel(new GridLayout(3, 1));
insideControlPanel.add(enterPanel);
insideControlPanel.add(setBPMButton);
insideControlPanel.add(buttonPanel);
controlPanel.add(insideControlPanel);
bpmLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
bpmOutputLabel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
controlFrame.getRootPane().setDefaultButton(setBPMButton);
controlFrame.getContentPane().add(controlPanel, BorderLayout.CENTER);
controlFrame.pack();
controlFrame.setVisible(true);
}
public void enableStopMenuItem() {
stopMenuItem.setEnabled(true);
}
public void disableStopMenuItem() {
stopMenuItem.setEnabled(false);
}
public void enableStartMenuItem() {
startMenuItem.setEnabled(true);
}
public void disableStartMenuItem() {
startMenuItem.setEnabled(false);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == setBPMButton) {
int bpm = Integer.parseInt(bpmTextField.getText());
controller.setBPM(bpm);//视图的改变会直接传递给控制器
} else if (event.getSource() == increaseBPMButton) {
controller.increaseBPM();
} else if (event.getSource() == decreaseBPMButton) {
controller.decreaseBPM();
}
}
public void updateBPM() {//模型发生改变时,这个方法会被调用
if (model != null) {
int bpm = model.getBPM();
if (bpm == 0) {
if (bpmOutputLabel != null) {
bpmOutputLabel.setText("offline");
}
} else {
if (bpmOutputLabel != null) {
bpmOutputLabel.setText("Current BPM: " + model.getBPM());
}
}
}
}
public void updateBeat() {//相应的,当模型开始一个新的节拍时,这个方法会被调用
if (beatBar != null) {
beatBar.setValue(100);
}
}
}
有了视图,有了模型,我们要构建控制器,使得视图更加聪明,我们使用策略模式,从控制器接口开始设计:
public interface ControllerInterface {
void start();
void stop();
void increaseBPM();
void decreaseBPM();
void setBPM(int bpm);
}
根据这个接口,我们实现这个控制器:
public class BeatController implements ControllerInterface {
BeatModelInterface model;//MVC中,控制器在中间,所以要同时持有模型以及视图的引用。
DJView view;
public BeatController(BeatModelInterface model) {
this.model = model;
view = new DJView(this, model);//控制器创建视图
view.createView();
view.createControls();
view.disableStopMenuItem();
view.enableStartMenuItem();
model.initialize();
}
public void start() {//控制器在得到start指令时去操纵模型和视图,下边的几个动作同理。
model.on();
view.disableStartMenuItem();//注意,控制器这时在帮视图做决定,视图只知道如何将菜单项变成开或者关而不知道在何时该这么做
view.enableStopMenuItem();
}
public void stop() {
model.off();
view.disableStopMenuItem();
view.enableStartMenuItem();
}
public void increaseBPM() {//控制器扩展了模型的动作
int bpm = model.getBPM();
model.setBPM(bpm + 1);
}
public void decreaseBPM() {
int bpm = model.getBPM();
model.setBPM(bpm - 1);
}
public void setBPM(int bpm) {
model.setBPM(bpm);
}
}
搞定!我们写一段测试代码来使用我们自己的MVC,先创建一个模型,然后创建一个控制器,将模型传入其中,控制器创建视图:
public class DJTestDrive {
public static void main (String[] args) {
BeatModelInterface model = new BeatModel();
ControllerInterface controller = new BeatController(model);
}
}
5.我们现在利用这个MVC模型完成另一项工作:心脏监视。我们希望将HeartModel适配成BeatModel
首先我们更换一下模型:
public interface HeartModelInterface {
int getHeartRate();
void registerObserver(BeatObserver o);
void removeObserver(BeatObserver o);
void registerObserver(BPMObserver o);
void removeObserver(BPMObserver o);
}
此时,我们需要知道视图只知道getBPM而不知道getHeartRate,那么这就需要我们使用适配器模式进行适配了。这就引出了一个MVC中重要的技巧:
使用适配器将模型适配成符合现有视图和控制器的需要的模型。
public class HeartAdapterimplements BeatModelInterface{//适配器要对被适配的接口进行实现,也就是那个在Client中被直接使用的部分
HeartModelInterface heart;//适配器中要保留另一部分的引用
public HeartAdapter(HeartModelInterface heart) {
this.heart = heart;
}
public void initialize() {}//不需要的部分我们在适配器中留空。
public void on() {}
public void off() {}
public int getBPM() {
return heart.getHeartRate();//适配器在此处运转
}
public void setBPM(int bpm) {}
public void registerObserver(BeatObserver o) {//将注册观察者Server的方法委托给heart
heart.registerObserver(o);
}
public void removeObserver(BeatObserver o) {
heart.removeObserver(o);
}
public void registerObserver(BPMObserver o) {
heart.registerObserver(o);
}
public void removeObserver(BPMObserver o) {
heart.removeObserver(o);
}
}
适配器ready以后,我们可以完成控制器了:
public class HeartController implements ControllerInterface {
HeartModelInterface model;
DJView view;
public HeartController(HeartModelInterface model) {
this.model = model;
view = new DJView(this, new HeartAdapter(model)); //用适配器进行包装
view.createView();
view.createControls();
view.disableStopMenuItem();
view.disableStartMenuItem();
}
public void start() {} //没有实际作用的我们留空
public void stop() {}
public void increaseBPM() {}
public void decreaseBPM() {}
public void setBPM(int bpm) {}
}
我们现在就可以写一段测试代码了:
public class HeartTestDrive {
public static void main (String[] args) {
HeartModel heartModel = new HeartModel();//首先创建模型
ControllerInterface model = new HeartController(heartModel);//然后创建控制器,控制器中创建了视图
}
}
6.最后我们提一句:在Web开发中,MVC被经常叫做Model 2。有了这个模型,该编程的人就去做编程,该做网页的人就去做网页。JSP只知道会从控制器收到一个Bean。在这个场景中,其实Bean其实就是模型,而且JSP只用到这个bean的BPM属性。
分享到:
相关推荐
HeadFirst设计模式学习笔记比较全面详细地讲解了13个设计模式,有利于大家更好的学习HeadFirst设计模式,希望亲们会喜欢~~~
Head First 设计模式学习笔记。更多内容请参见文章内容。
HeadFirst 设计模式学习笔记3--装饰模式 Demo http://blog.csdn.net/laszloyu/archive/2010/05/12/5582561.aspx
HeadFirst 设计模式学习笔记2--观察者模式 demo http://blog.csdn.net/laszloyu/archive/2010/05/12/5581769.aspx
笔记_HeadFirst设计模式
http://blog.csdn.net/laszloyu/archive/2010/05/11/5579765.aspx 示例代码
Head First设计模式(完整高清版).pdf,涵盖了各种宝贵的设计思想!
经典的设计模式书籍打包:Head First 设计模式 +Java设计模式(第2版)
Head First 设计模式(高清中文完整版带目录)+附书源码+HeadFirst设计模式学习伴侣.rar 又名: Head First Design Patterns 作者: (美)弗里曼(Freeman,E.) 副标题: Head First Design Patterns 简介 ·····...
head first 设计模式 高清中文版 pdf
HeadFirst设计模式学习伴侣.jpg
如果你曾经读过任何一本深入浅出(Head First)系列书籍,你就会知道能够从本书中得到的是:透过丰富的视觉效果让你的大脑充分地运作。本书的编写运用许多最新的研究,包括神经生物学、认知科学以及学习理论,这使得...
Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First...
HeadFirst设计模式 读书 笔记
Head.First 设计模式学习笔记.pdf Head.First 设计模式学习笔记.pdf
1.1 《Head First设计模式》阅读笔记.第一章 1.2 《Head First设计模式》阅读笔记.第二章 1.3 《Head First设计模式》阅读笔记.第三章 1.4 《Head First设计模式》阅读笔记.第四章 1.5 《Head First设计模式》...
Head First设计模式中文版 Head First设计模式中文版 Head First设计模式中文版
head first设计模式学习代码,代码都是自己学习过程中写的,记录学习的脚步
Head First设计模式 Head First设计模式 Head First设计模式
head first设计模式 下载地址 head first设计模式 下载地址 head first设计模式 下载地址 head first设计模式 下载地址 head first设计模式 下载地址