2013年8月4日 《Head First设计模式学习》
今天来介绍这本书最后一个模式——复合模式,当然设计模式可不仅仅只有那么多,经过前辈们演变出来的模式可是很多的,我所介绍的只是比较通用的模式,可以说是一种规范吧,我想在实际的工作中,简单的一种模式是不能满足项目千奇百怪的需求的,那就可能需要使用多种模式的组合来满足了,本篇博客主要介绍的就是MVC模式,这是复合模式的经典模式,我想做Java Web开发的开发者对这个模式一定不陌生,这个模式给开发带来大大的便利,把它分为了M(Model)、V(View)、C(Control)三层,让设计变得干净又有弹性。
复合模式——复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。
跟以往一样回顾以往的知识:
———————————————————————————————————————————————————
上面这幅图描述的就是MVC模式,下面根据这幅图对MVC进行一下解释。
1、你是用户—你和视图交互
视图是模型的窗口。当你对视图做一些事事(比方说:按下“播放”按钮),视图就告诉控制器你做了什么。控制器会负责处理。
2、控制器要求模型改变状态。
控制器解读你的动作。如果你按下某个按钮,控制器会理解这个动作的意义,并告知模型如何做出对应的动作。
3.控制器也可能要求视图做改变。
当控制器从视图接收到某一动作,结构可能是它也需要告诉视图改变其结果。比方说,控制器可以将界面上的某些按钮或菜单项变成有效或无效。
4.当模型发生改变时,模型会通知视图。
不管是你做了某些动作(比方说按下按钮)还是内部有了某些改变(比方说播放清单的下一首歌开始)只要当模型内的东西改变时,模型都会通知视图它的状态改变了。
5.视图向模型询问状态。
视图直接从模型取得它显示的状态。比方说,当模型通知视图新歌开始播放,视图向模型询问歌名并显示出来。当控制器请求视图改变时,视图也可能向模型询问某些状态。
戴着模式的有色眼镜看MVC
MVC使用哪些模式呢,由哪些模式组成的呢?
使用了:
1.策略模式
视图和控制器实现了策略模式:视图是一个对象,可以被调整使用不同的策略,而控制提供了策略。视图只关心系统中可视的部分,对与任何界面行为,都委托给控制器处理。使用策略模式也可以让视图和模型之间关系解耦,因为控制器负责和模型交互来传递用户的请求。对与工作是怎么完成的,视图豪不知情。
2.观察者模式
模型实现了观察者模式,当状态改变时,相关对象将持续更新。使用观察者模式,可以让模型完全独立于视图和控制器。同一个模型可以使用不同的视图,甚至可以同时使用多个视图。
3.组合模式
显示包括了窗口、面板、按钮、文本标签等。每个显示组件如果不是组合节点(例如窗口),就是叶节点(例如按钮)。当控制器告诉视图更新时,只需告诉视图最顶层的组件即可,组合会处理其余的事。
以上就是关于MVC使用各种模式的说明,下面来看一个例子介绍本篇博客。
利用MVC控制节拍
BeatModelInterface.java
package combined; public interface BeatModelInterface { void initialize(); //在BeatModel被初始化之后,就会调用此方法 void on(); //打开节拍器 void off(); //关闭节拍器 void setBPM(int bpm); //设置bmp值 int getBPM(); //获得当前bmp值 void registerObserver(BeatObserver o); void removeObserver(BeatObserver o); void registerObserver(BPMObserver o); void removeObserver(BPMObserver o); }
BeatModel.java
package combined; import java.util.ArrayList; import javax.sound.midi.MetaEventListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiSystem; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.Track; public class BeatModel implements BeatModelInterface, MetaEventListener { Sequencer sequencer; //定序器(Sequencer)对象知道如何产生真实的节拍 ArrayList beatObservers = new ArrayList(); //两种观察者(一种观察节拍,一种观察BPM改变) 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; //设置BPM实例变量 sequencer.setTempoInBPM(getBPM()); //要求定序器改变BPM notifyBPMObservers(); //通知所有的BPM观察者,BPM已经改变 } 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; } }
观察者
BeatObserver.java
package combined; public interface BeatObserver { void updateBeat(); }
package combined; public interface BPMObserver { void updateBPM(); }
BeatBar.java
package combined; import javax.swing.JProgressBar; public class BeatBar extends JProgressBar implements Runnable { JProgressBar progressBar; Thread thread; public BeatBar() { thread = new Thread(this); setMaximum(100); thread.start(); } @Override public void run() { for(;;) { int value = getValue(); value = (int)(value * 0.75); setValue(value); repaint(); try { Thread.sleep(50); } catch (Exception e) { e.printStackTrace(); } } } }
package combined; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.imageio.plugins.bmp.BMPImageWriteParam; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; /** * 视图类,它是一个观察者,同时关心实时节拍和BPM的改变 * @author Administrator * */ public class DJView implements ActionListener, BeatObserver, BPMObserver { 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); } } }
package combined; public interface ControllerInterface { void start(); void stop(); void increaseBPM(); void decreaseBPM(); void setBPM(int bpm); }
package combined; /** * 控制器的实现 * @author Administrator * */ public class BeatController implements ControllerInterface { BeatModelInterface model; DJView view; public BeatController(BeatModelInterface model) { this.model = model; view = new DJView(this, model); view.createView(); view.createControls(); view.disableStopMenuItem(); view.disableStartMenuItem(); model.initialize(); } @Override public void start() { model.on(); view.disableStartMenuItem(); view.enableStopMenuItem(); } @Override public void stop() { model.off(); view.disableStopMenuItem(); view.enableStartMenuItem(); } @Override public void increaseBPM() { int bpm = model.getBPM(); model.setBPM(bpm + 1); } @Override public void decreaseBPM() { int bpm = model.getBPM(); model.setBPM(bpm - 1); } @Override public void setBPM(int bpm) { model.setBPM(bpm); } }
DJTestDrive.java
package combined; public class DJTestDrive { public static void main (String[] args) { BeatModelInterface model = new BeatModel(); ControllerInterface controller = new BeatController(model); } }
效果如下图:
关于MVC模式就说到这里,下一篇模式见。