设计模式学习--复合模式(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模式

———————————————————————————————————————————————————

设计模式学习--复合模式(Compound Pattern)





上面这幅图描述的就是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);

    }

}


 

效果如下图:

    设计模式学习--复合模式(Compound Pattern)


关于MVC模式就说到这里,下一篇模式见。


 

你可能感兴趣的:(Pattern)