设计模式学习--复合模式(Compound Pattern)
概述
———————————————————————————————————————————————————
2013年8月4日《Head First设计模式学习》
今天来介绍这本书最后一个模式——复合模式,当然设计模式可不仅仅只有那么多,经过前辈们演变出来的模式可是很多的,我所介绍的只是比较通用的模式,可以说是一种规范吧,我想在实际的工作中,简单的一种模式是不能满足项目千奇百怪的需求的,那就可能需要使用多种模式的组合来满足了,本篇博客主要介绍的就是MVC模式,这是复合模式的经典模式,我想做Java Web开发的开发者对这个模式一定不陌生,这个模式给开发带来大大的便利,把它分为了M(Model)、V(View)、C(Control)三层,让设计变得干净又有弹性。
复合模式——复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。
跟以往一样回顾以往的知识:
OO原则
———————————————————————————————————————————————————
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦合设计而努力
- 类应该对扩展开放,对修改关闭
- 依赖抽象,不要以来具体类
- 只和朋友交谈
- 别找我,我会找你
- 类应该只有一个改变的理由
要点
———————————————————————————————————————————————————
- MVC是复合模式,结合观察者模式、策略模式和组合模式。
- 模式使用观察者模式,以便观察者更新,同时保持两者之间解耦。
- 控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为。
- 视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮。
- 这些模式携手合作,把MVC模式的三层解耦,这样可以保持设计干净又有弹性。
- 适配器模式用来将新的模型适配成已有的视图和控制器。
- Model 2是MVC在Web上的应用。
- 在Model 2中,控制器实现成Servlet,而JSP/HTML实现视图
MVC模式
———————————————————————————————————————————————————
上面这幅图描述的就是MVC模式,下面根据这幅图对MVC进行一下解释。
1、你是用户—你和视图交互
视图是模型的窗口。当你对视图做一些事事(比方说:按下“播放”按钮),视图就告诉控制器你做了什么。控制器会负责处理。
2、控制器要求模型改变状态。
控制器解读你的动作。如果你按下某个按钮,控制器会理解这个动作的意义,并告知模型如何做出对应的动作。
3.控制器也可能要求视图做改变。
当控制器从视图接收到某一动作,结构可能是它也需要告诉视图改变其结果。比方说,控制器可以将界面上的某些按钮或菜单项变成有效或无效。
4.当模型发生改变时,模型会通知视图。
不管是你做了某些动作(比方说按下按钮)还是内部有了某些改变(比方说播放清单的下一首歌开始)只要当模型内的东西改变时,模型都会通知视图它的状态改变了。
5.视图向模型询问状态。
视图直接从模型取得它显示的状态。比方说,当模型通知视图新歌开始播放,视图向模型询问歌名并显示出来。当控制器请求视图改变时,视图也可能向模型询问某些状态。
戴着模式的有色眼镜看MVC
MVC使用哪些模式呢,由哪些模式组成的呢?
使用了:
1.策略模式
视图和控制器实现了策略模式:视图是一个对象,可以被调整使用不同的策略,而控制提供了策略。视图只关心系统中可视的部分,对与任何界面行为,都委托给控制器处理。使用策略模式也可以让视图和模型之间关系解耦,因为控制器负责和模型交互来传递用户的请求。对与工作是怎么完成的,视图豪不知情。
2.观察者模式
模型实现了观察者模式,当状态改变时,相关对象将持续更新。使用观察者模式,可以让模型完全独立于视图和控制器。同一个模型可以使用不同的视图,甚至可以同时使用多个视图。
3.组合模式
显示包括了窗口、面板、按钮、文本标签等。每个显示组件如果不是组合节点(例如窗口),就是叶节点(例如按钮)。当控制器告诉视图更新时,只需告诉视图最顶层的组件即可,组合会处理其余的事。
以上就是关于MVC使用各种模式的说明,下面来看一个例子介绍本篇博客。
利用MVC控制节拍
BeatModelInterface.java
- package combined;
-
- public interface BeatModelInterface {
-
- void initialize();
- void on();
- void off();
- void setBPM(int bpm);
- int getBPM();
- 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;
- 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;
- }
- }
观察者
BeatObserver.java
- package combined;
-
- public interface BeatObserver {
- void updateBeat();
- }
BPMObserver.java
- 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();
- }
- }
- }
-
- }
DJView.java
- 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;
-
-
-
-
-
-
- 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() {
-
- 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() {
-
- 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);
- }
BeatController.java
- package combined;
-
-
-
-
-
- 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模式就说到这里,下一篇模式见。