zoukankan      html  css  js  c++  java
  • 设计模式学习--复合模式(Compound Pattern)

    设计模式学习--复合模式(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(); //在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();
    }
    


    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;
    
    /**
     * 视图类,它是一个观察者,同时关心实时节拍和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);
    }
    


    BeatController.java

    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模式就说到这里,下一篇模式见。


  • 相关阅读:
    Fast data loading from files to R
    php的循环与引用的一个坑
    让我安静的写会儿代码
    chrome一个奇怪的问题
    用原生js给DOM元素添加、删除一个类名
    在ie浏览器下背景图片不显示的解决办法
    伪元素选择器之 ::placeholder
    基础版放大镜--面向对象
    元素尺寸大全
    如何解决PC端和移动端自适应问题?
  • 原文地址:https://www.cnblogs.com/riskyer/p/3236964.html
Copyright © 2011-2022 走看看