设计模式十二之MVC模式(Java)

版权声明:分享才能发挥最大的价值 https://blog.csdn.net/qq_32252957/article/details/82732394

这是我看Head first设计模式书籍之后想要总结的知识点,一方面是对自己学习的东西总结和提炼加强自己的理解和记忆,另一方面是给大家简化这本书,方便大家快速了解各种设计模式。

我想提醒大家的是,设计模式只是前人总结的一些经验套路,实际上还是要在开发项目中慢慢体会,不可成为设计模式的中毒患者,强行照搬设计模式的一些规则。

下面是我上传github的完整的代码,欢迎Follow偶。https://github.com/youaresherlock/JavaDesignPattern

上节我们简单讲解了MVC的基本概念,现在我们以一个实际的Java GUI 播放MIDI音乐程序案例来进一步了解MVC模式

先说说我们程序中视图,控制器和模型的作用

视图,用来呈现模型。视图通常直接从模型中取得它需要显示的状态和数据(模型提供接口视图调用就行了,一般getter方法等即可),视图持有模型和控制器的引用,这样可以调用他们的方法。视图实现了观察者接口,是观察者,模型中可以注册观察者,观察模型BPM和Beat的变化,视图再根据变化改变界面。

控制器: 用户在视图上进行点击事件触发,调用控制器对应的方法来让控制器进一步解读。控制器的作用就是解耦模型和视图。

模型: 模型持有所有的数据、状态和程序逻辑。模型没有注意到视图和控制器,虽然它提供了操纵和检索状态的接口,并发送状态改变通知给观察者(视图)。

使用了哪些模式?

组合模式: 视图显示了包括窗口、面板、按钮、文本标签等。每个显示组件如果不是组合结点(如窗口、面板),就是叶节点(如按钮).当控制器告诉视图更新时,只需告诉视图最顶层的组件即可,组合会处理其余的事。

策略模式: 视图和控制器实现了经典的策略模式,视图是一个对象,可以被调整使用不同的策略,而控制器提供了策略。视图只关心系统中可视的部分,对于任何界面行为,都委托给控制器处理。使用策略模式也可以让视图和模型之间的关系解耦,因为控制器负责和模型交互来传递用户的请求。对于工作如何完成的,视图毫不知情。控制器有控制器接口,而由具体控制器实现这个接口形成算法簇,这样体现了策略模式。

观察者模式: 模型实现了观察者模式,当状态改变时,相关对象将持续更新。使用观察者模式,可以让模型完全独立于视图和控制器。同一个模型可以使用不同的视图,甚至可以同时使用多个视图。

我们看代码部分

模型接口BeatModelInterface.java

package headfirst.designpatterns.combined.djview;
 
/*
模型使用观察者模式,我们需要让对象注册成为观察者并发送出通知
*/
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); // 注册成为BPM观察者
  
	void removeObserver(BPMObserver o);
}

模型具体实现BeatModel.java

package com.gougoucompany.designpattern.mvc;

import java.util.ArrayList;
import java.util.Iterator;

import javax.sound.midi.InvalidMidiDataException;
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;
import javax.xml.ws.handler.MessageContext;

/*
模型: 持有所有的数据、状态和程序逻辑
The MetaEventListener interface should be implemented by classes whose instances need to be notified when a Sequencer 
has processed a MetaMessage. To register a MetaEventListener object to receive such notifications, pass it as the
 argument to the addMetaEventListener method of Sequencer.
 Sequencer处理源信息时,BeatModel实例需要被通知
void	meta​(MetaMessage meta)	这个接口只有一个抽象方法,
	Invoked when a Sequencer has encountered and processed a MetaMessage in the Sequence it is processing.
*/
public class BeatModel implements BeatModelInterface, MetaEventListener {
	Sequencer sequencer;
	ArrayList<BeatObserver> beatObservers = new ArrayList<>();
	ArrayList<BPMObserver> bpmObservers = new ArrayList<>();
	int bpm = 90;
	Sequence sequence;
	Track track;	

	@Override
	public void initialize() {
		// TODO Auto-generated method stub
		setUpMidi();
		buildTrackAndStart();
	}
	
	public void setUpMidi() {
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
			sequence = new Sequence(Sequence.PPQ, 4);
			track = sequence.createTrack();
			sequencer.setTempoInBPM(getBPM());
			sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
		} catch(Exception e){ 
			e.printStackTrace();
		}
	}
	
	public void buildTrackAndStart() {
		int[] trackList = {35, 0, 46, 0};
		
		sequence.deleteTrack(null);
		track = sequence.createTrack();
		makeTracks(trackList);
		
		MetaMessage metaMessage = new MetaMessage();
		String text = "a metaEvent maker";
		try {
			metaMessage.setMessage(47, text.getBytes(), text.length());
		} catch (InvalidMidiDataException e1) {
			e1.printStackTrace();
		}
		track.add(new MidiEvent(metaMessage, 3)); //这里是为了产生MetaEvent事件,Type设置为47,这样脉动柱进度条就可以100->0 100->0的动态形式
		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 shortMessage = new ShortMessage(); 
			shortMessage.setMessage(comd, chan, one ,two);
			event = new MidiEvent(shortMessage, tick);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return event;
	}

	@Override
	public void on() {
		// TODO Auto-generated method stub
		System.out.println("Starting the sequencer");
		sequencer.start();
		setBPM(90);
	}

	@Override
	public void off() {
		// TODO Auto-generated method stub
		setBPM(0);
		sequencer.stop();
	}

	@Override
	public void setBPM(int bpm) {
		// TODO Auto-generated method stub
		this.bpm = bpm;
		sequencer.setTempoInBPM(bpm);
		notifyBPMObservers();
	}

	@Override
	public int getBPM() {
		// TODO Auto-generated method stub
		return bpm;
	}
	
	//新的节拍开始,通知观察者
	void beatEvent() {
		notifyBeatObservers();
	}

	@Override
	public void registerObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		beatObservers.add(o);
	}

	@Override
	public void removeObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		int i = beatObservers.indexOf(o); //元素不存在会返回-1
		if(i >= 0) {
			beatObservers.remove(i);
		}
	}
	
	public void notifyBeatObservers() {
		//可以使用三种基本的遍历方法
		Iterator<BeatObserver> iterator = beatObservers.iterator();
		while(iterator.hasNext()) {
			BeatObserver observer = (BeatObserver)iterator.next();
			observer.updateBeat();
		}
		
//		for(BeatObserver observer : beatObservers) {
//			observer.updateBeat(); //for-each 使用的还是内置的迭代器
//		}
//		
//		for(int i = 0; i < beatObservers.size(); i ++) {
//			BeatObserver observer = beatObservers.get(i);
//			observer.updateBeat();
//		}
	}

	@Override
	public void registerObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		bpmObservers.add(o);
	}

	@Override
	public void removeObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		int i = bpmObservers.indexOf(o);
		if(i >= 0) {
			bpmObservers.remove(i);
		}
	}
	
	public void notifyBPMObservers() {
		Iterator<BPMObserver> iterator = bpmObservers.iterator();
		while(iterator.hasNext()) {
			BPMObserver observer = iterator.next();
			observer.updateBPM();
		}
	}

	@Override
	public void meta(MetaMessage message) {
		// TODO Auto-generated method stub
		if(message.getType() == 47) {
			beatEvent(); //节拍改变通知观察者
			sequencer.start();
			setBPM(getBPM()); //bpm可能改变,通知所有的观察者
		}
	}

}

视图

我们实现要用两个分离的窗口;一个窗口包含当前的BPM和脉动柱(把模型的视图从控制的视图分离),另一个则包含界面控制。

视图实现了两个观察者接口,对模型BPM和Beat节拍的观察

BeatObserver.java

package com.gougoucompany.designpattern.mvc;

//节拍观察者接口
public interface BeatObserver {
	void updateBeat();
}

BPMObserver.java

package com.gougoucompany.designpattern.mvc;

//BPM观察者接口
public interface BPMObserver {
	void updateBPM();
}

视图DJView.java

package com.gougoucompany.designpattern.mvc;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//DJView是一个观察者,同时关心实时节拍和BPM的改变

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; //bpm显示标签
	JTextField bpmTextField; //bpm文本框
	JButton setBPMButton; //设置bpm按钮
	JButton increaseBPMButton; //增加bpm按钮
	JButton decreaseBPMButton; //减少bpm按钮
	JMenuBar menuBar; //菜单栏
	JMenu menu; //菜单
	JMenuItem startMenuItem; //开始菜单项
	JMenuItem stopMenuItem; //停止菜单项
	
	public DJView(ControllerInterface controller, BeatModelInterface model) {
		this.controller = controller;
		this.model = model;
		//注册成为BeatObserver和BPMObserver观察者
		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(500,  400));
		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.setSize(600, 400);
		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() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				controller.start(); ////控制器调用start方法,模型开始控制播放音乐过程的逻辑
			}
		});
		stopMenuItem = new JMenuItem("Stop");
		menu.add(stopMenuItem); //添加停止菜单项
		stopMenuItem.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				controller.stop();
			}
		});
		JMenuItem exit = new JMenuItem("Quit"); //添加退出菜单
		exit.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				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("<<");
		//注册本类监听set,<<,>>点击事件的监听
		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.setSize(800, 400);
		controlFrame.setVisible(true);
	}

	/*
            模型BPM状态改变时,updateBPM()方法会被调用。
            这时候我们更新当前BPM的显示。我们可以通过直接请求模型而得到这个值
    */
	@Override
	public void updateBPM() {
		// TODO Auto-generated method stub
		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());
				}
			}
		}
	}

	/**
	 * 模型开始一个新的节拍时,updateBeat()方法会被调用。这时候,我们让脉动柱跳一下,我们设置
	 * 为100,让脉动柱自行处理动画部分。
	 */
	@Override
	public void updateBeat() {
		// TODO Auto-generated method stub
		if(beatBar != null) {
			beatBar.setValue(100);
		}
	}
	
	/*
	 * 这些方法将菜单中的start和stop项变成enable或disable,
	 * 控制器可以利用这些接口方法改变用户界面
	 */
	public void enableStopMenuItem() {
		stopMenuItem.setEnabled(true);
	}
	
	public void disableStopMenuItem() {
		stopMenuItem.setEnabled(false);
	}
	
	public void enableStartMenuItem() {
		startMenuItem.setEnabled(true);
	}

	public void disableStartMenuItem() {
		startMenuItem.setEnabled(false);
	}
	
	//不同按钮被单击,将信息传给控制器来进一步改变模型的
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if(e.getSource() == setBPMButton) {
			//如果bpmTextField不为空,并且内容不是空字符串
			if(bpmTextField != null &&  !bpmTextField.getText().isEmpty()) {
				int bpm = Integer.parseInt(bpmTextField.getText());
				controller.setBPM(bpm);
			} 
		} else if(e.getSource() == increaseBPMButton) {
			controller.increaseBPM();
		} else if(e.getSource() == decreaseBPMButton) {
			controller.decreaseBPM();
		}
	}
}



脉动柱组件继承了JProcessBar,又实现了Runnable接口,因为我们希望他的进度可以自身实时变化(当模型Sequencer处理MetaMessage的时候,就会触发实现的meta()方法,来通知视图来进行对脉动柱进度的设置。这样脉动柱就会一直从最大Max100到接近0不断变化跳动)

BeatBar.java

package com.gougoucompany.designpattern.mvc;

import javax.swing.JProgressBar;

//脉动柱组件
public class BeatBar extends JProgressBar implements Runnable{
	Thread thread;
	
	public BeatBar() {
		thread = new Thread(this);
		//设置最大值,继承下来的方法
		setMaximum(100);
		thread.start();
	}

	//线程的run方法是一个死循环,因此始终监听脉动柱的数值变化,来进行进度条的设置
	@Override
	public void run() {
		for(;;) {
			int value = getValue();
			System.out.println("进度条的值是:" + value);
			value = (int)(value * .75); //从设置的值开始缩减到接近0
			setValue(value);
			repaint();
			try {
				Thread.sleep(50);//每50毫秒更新一次
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			
		}
	}

}

控制器是策略

控制器接口ControllInterface.java

package com.gougoucompany.designpattern.mvc;
/*
控制器是策略,我们将控制器传入视图的构造器中,
视图可以调用下面所有的方法,实现了ControllerInterface就是一系列算法簇,
这样没有依赖,可以随时替换不同控制器的实现类
*/
public interface ControllerInterface {
	void stop();
	void increaseBPM();
	void decreaseBPM();
	void setBPM(int bpm);
	void start();
}

控制器

package com.gougoucompany.designpattern.mvc;

public class BeatController implements ControllerInterface {

	/*
	控制器在MVC设计中起到桥梁的作用,所以它拥有模型和视图的实例
	*/
	BeatModelInterface model;
	DJView view;
	
	public BeatController(BeatModelInterface model) {
		this.model = model;
		//创建实例并初始化视图和模型
		view = new DJView(this, model);
		view.createView();
		view.createControls();
		view.disableStopMenuItem();
		view.enableStartMenuItem();
		model.initialize();
	}
	
	@Override
	public void start() {
		// TODO Auto-generated method stub
		model.on();
		view.disableStartMenuItem();
		view.enableStopMenuItem();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		model.off();
		view.disableStopMenuItem();
		view.enableStartMenuItem();
	}

	@Override
	public void increaseBPM() {
		// TODO Auto-generated method stub
		int bpm = model.getBPM();
		model.setBPM(bpm + 1);
	}

	@Override
	public void decreaseBPM() {
		// TODO Auto-generated method stub
		int bpm = model.getBPM();
		model.setBPM(bpm - 1);
	}

	@Override
	public void setBPM(int bpm) {
		// TODO Auto-generated method stub
		model.setBPM(bpm);
	}

}

测试类

package com.gougoucompany.designpattern.mvc;

public class DJTestDrive {
	public static void main(String args[]) {
		BeatModelInterface model = new BeatModel();
		ControllerInterface controller = new BeatController(model);
	}
}

下面是程序运行图:

这是程序一开始运行,初始化的时候图,此时左边提示offline不在线

在DJControl菜单栏点击展开菜单,菜单中点击菜单项Start,可以看到BPM为90,脉动柱持续跳动

在文本标签框输入200,点击set按钮,左边视图变为200,同时脉动柱持续跳动

下面是点击Start菜单项之后,可以看到Start不能选择,其他两个可以选择

点击停止菜单项,同时可以看到被禁止重新选择,其他两个可以选择。

让我们再一次体会这种设计的强大之处

比如我们现在有一个心脏监测类,类图如下

HeartModelInterface.java

package headfirst.designpatterns.combined.djview;

public interface HeartModelInterface {
	int getHeartRate();
	void registerObserver(BeatObserver o);
	void removeObserver(BeatObserver o);
	void registerObserver(BPMObserver o);
	void removeObserver(BPMObserver o);
}
HeartModel
getHeartRate()
registerBeatObserver()
registerBPMObserver()
//心脏的其他方法

如果HeartModel可以复用我们当前的视图,这样会省下不少功夫。我们同时也要一个控制器和这个模型一起运作。另外

HeartModel的接口并不符合视图的期望,因为它的方法是getHeartRate(),而不是getBPM()。如何设计一些类,让视图和

HeartModel能够搭配在一起使用呢?

我们现在要用适配器模式,将HeartModel适配成BeatModel.使用适配器将模型适配成符合现有视图和控制器的需要的模型。

HeartModel.java

package com.gougoucompany.designpattern.mvc;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
public class HeartModel implements HeartModelInterface, Runnable {
	ArrayList<BeatObserver> beatObservers = new ArrayList<>();
	ArrayList<BPMObserver> bpmObservers = new ArrayList<>();
	int time = 1000;
	int bpm = 90;
	Random random = new Random(System.currentTimeMillis());
	Thread thread;
	
	public HeartModel() {
		thread = new Thread(this);
		thread.start();
	}
 
	@Override
	public void run() {
		// TODO Auto-generated method stub
		int lastrate = -1;
		
		for(;;) {
			int change = random.nextInt(10);
			if(random.nextInt(2) == 0) {
				change = 0 - change;
			}
			int rate = 60000 / (time + change);
			if(rate < 120 && rate > 50) {
				time += change;
				notifyBeatObservers();
				if(rate != lastrate) {
					lastrate = rate;
					notifyBPMObservers();
				}
			}
			try {
				Thread.sleep(time);
			} catch(Exception e) {}
		}
	}

	@Override
	public int getHeartRate() {
		// TODO Auto-generated method stub
		return (int)6000/time;
	}

	@Override
	public void registerObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		beatObservers.add(o);
	}

	@Override
	public void removeObserver(BeatObserver o) {
		// TODO Auto-generated method stub
		int i = beatObservers.indexOf(o);
		if(i >= 0) {
			beatObservers.remove(i);
		}
	}
	
	public void notifyBeatObservers() {
		Iterator<BeatObserver> iterator = beatObservers.iterator();
		while(iterator.hasNext()) {
			BeatObserver observer = (BeatObserver)iterator.next();
			observer.updateBeat();
		}
	}

	@Override
	public void registerObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		bpmObservers.add(o);
	}

	@Override
	public void removeObserver(BPMObserver o) {
		// TODO Auto-generated method stub
		int i = bpmObservers.indexOf(o);
		if(i >= 0) {
			bpmObservers.remove(i);
		}
	}
	
	public void notifyBPMObservers() {
		Iterator<BPMObserver> iterator = bpmObservers.iterator();
		while(iterator.hasNext()) {
			BPMObserver observer = (BPMObserver) iterator.next();
			observer.updateBPM();
		}
	}

}

HeartAdapter.java

package headfirst.designpatterns.combined.djview;

public class HeartAdapter implements BeatModelInterface {
	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) {
		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);
	}
}

HeartController.java 创建控制器,并让视图和HeartModel整合起来,这就是复用

package com.gougoucompany.designpattern.mvc;

public class HeartController implements ControllerInterface {
	HeartModelInterface model;
	DJView view;
	
	public HeartController(HeartModelInterface model) {
		this.model = model;
		//HeartModel不能直接交给视图,必须要先用适配器包装过才行。
		view = new DJView(this,  new HeartAdapter(model));
		view.createView();
		view.createControls();
		//按钮都不需要,设置为disabled
		view.disableStopMenuItem();
		view.disableStartMenuItem();
	}

	//下面五个方法没有实际的作用,因此函数体中为空
	@Override
	public void stop() {
		// TODO Auto-generated method stub

	}

	@Override
	public void increaseBPM() {
		// TODO Auto-generated method stub

	}

	@Override
	public void decreaseBPM() {
		// TODO Auto-generated method stub

	}

	@Override
	public void setBPM(int bpm) {
		// TODO Auto-generated method stub

	}

	@Override
	public void start() {
		// TODO Auto-generated method stub

	}

}

测试代码HeartTestDrive.java

package com.gougoucompany.designpattern.mvc;

public class HeartTestDrive {
	public static void main(String args[]) {
		HeartModel heartModel = new HeartModel();
		ControllerInterface model = new HeartController(heartModel);
	}
}

可以看到检测心跳的脉动柱不断变化,所有按钮的事件并不起作用,因为控制器截断了

猜你喜欢

转载自blog.csdn.net/qq_32252957/article/details/82732394