`
0428loveyu
  • 浏览: 30770 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

GUI中的观察者模式

 
阅读更多

MVC模式对于用户界面的开发有着重要的意义,在Java中,不是按照标准的MVC模式实现的,而是将控制器和视图结合起来,而模型独立存在。模型成为观察者模式中的被观察对象,而控制器和视图则作为观察者。


下面是一个改自《重构》的例子,如下图所示:三个输入框之间的关系为start+length=end。 修改其中的任何一个输入框,都要保持这个关系恒成立。如果将数据模型和GUI界面、事件处理全部混在一个类,一样可以实现这个功能,但是代码将十分难懂,也难以维护和拓展。因此将数据模型独立出来。



首先定义数据模型类,数据模型提供接口操作,用于查询、修改或者其他更复杂的数据操作,供用户调用,代码如下:

/**
 * 
 */
package design.patterns.eventgenerator.observer;

import java.util.Observable;

/**
 * @author Brandon B. Lin
 * 
 */
public class TestModel extends Observable {

	private String end = "0";
	private String start = "0";
	private String length = "0";

	public String getEnd() {
		return end;
	}

	public void setEnd(String end) {
		this.end = end;
		setChanged();
		notifyObservers();
	}

	public String getStart() {
		return start;
	}

	public void setStart(String start) {
		this.start = start;
		setChanged();
		notifyObservers();
	}

	public String getLength() {
		return length;
	}

	public void setLength(String length) {
		this.length = length;
		setChanged();
		notifyObservers();
	}

	public void calculateLength() {
		try {
			int start = Integer.parseInt(getStart());
			int end = Integer.parseInt(getEnd());
			int length = end - start;
			setLength(String.valueOf(length));
		} catch (NumberFormatException exception) {
			exception.printStackTrace();
		}
	}

	public void calculateEnd() {
		try {
			int start = Integer.parseInt(getStart());
			int lenght = Integer.parseInt(getLength());
			int end = start + lenght;
			setEnd(String.valueOf(end));
		} catch (NumberFormatException exception) {
			exception.printStackTrace();
		}
	}

}
这个模型类继承了Observable抽象类,提供查询数据的get方法,同时提供修改数据的方法set,如果数据被修改,通知所有监听者(setChanged和notifyObservers方法)。除此以外,它还提供了逻辑操作接口calculateLength和calculateEnd。


接着定义用户界面,处理时间,同时与模型交互。代码如下:

/**
 * 
 */
package design.patterns.eventgenerator.observer;

import java.awt.GridLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

/**
 * @author Brandon B. Lin
 * 
 */
public class TestWindow extends JFrame implements Observer {

	private static final long serialVersionUID = 8232669335169364475L;
	private JTextField startField;
	private JTextField endField;
	private JTextField lengthField;

	private TestModel model;

	public TestWindow() {
		initFrame();
		initModel();
	}

	private void initFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle("Test Window");
		setSize(400, 300);
		add(createTextFieldPanel());
	}

	private void initModel() {
		model = new TestModel();
		model.addObserver(this);
		update(model, null);
	}

	private JPanel createTextFieldPanel() {
		JPanel textFieldPanel = new JPanel();
		textFieldPanel.setLayout(new GridLayout(3, 2));
		JLabel startLabel = new JLabel("Start: ");
		startField = new JTextField("0", 5);

		SymbolFocus listener = new SymbolFocus();
		startField.addFocusListener(listener);

		JLabel endLabel = new JLabel("End: ");
		endField = new JTextField("0", 5);
		endField.addFocusListener(listener);
		JLabel lengthLabel = new JLabel("Length: ");
		lengthField = new JTextField("0", 5);
		lengthField.addFocusListener(listener);

		textFieldPanel.add(startLabel);
		textFieldPanel.add(startField);
		textFieldPanel.add(endLabel);
		textFieldPanel.add(endField);
		textFieldPanel.add(lengthLabel);
		textFieldPanel.add(lengthField);

		return textFieldPanel;

	}

	// self encapsulate field
	public String getEnd() {
		return model.getEnd();
	}

	public void setEnd(String newValue) {
		model.setEnd(newValue);
	}

	public String getStart() {
		return model.getStart();
	}

	public void setStart(String newValue) {
		model.setStart(newValue);
	}

	public String getLength() {
		return model.getLength();
	}

	public void setLength(String newValue) {
		model.setLength(newValue);
	}

	@Override
	public void update(Observable observable, Object context) {
		endField.setText(model.getEnd());
		startField.setText(model.getStart());
		lengthField.setText(model.getLength());
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				new TestWindow().setVisible(true);
			}
		});

	}

	class SymbolFocus extends FocusAdapter {
		@Override
		public void focusLost(FocusEvent event) {
			Object source = event.getSource();
			if (source == lengthField) {
				setLength(lengthField.getText());
				model.calculateEnd();
			} else if (source == startField) {
				setStart(startField.getText());
				model.calculateLength();
			} else {
				setEnd(endField.getText());
				model.calculateLength();
			}
		}

	}

}

这个类实现了观察者接口Observer,在构造函数中,初始化用户界面,然后初始化模型。通过持有对模型类的引用来与模型类交互。当用户改变任何一个输入框的值的时候,作为响应,首先改变模型类中相应的数据,然后调用模型的业务逻辑,其结果也是改变了模型的数据,而一旦有模型数据改变,作为观察者的用户界面都会得到通知,得到通知后就可以更新界面。逻辑十分清晰。


《重构》中把这个称谓“Duplicate Observed Data”,其实就是通过观察者模式来保持数据模型和用户界面(数据的显示)之间的同步,是一种MVC变体。我们还看到在TestWindow类中,用到了“Self encapsulate field”重构方法,这一重构在这个例子中也许看不到什么好处,我们完全可以通过对model相关方法直接调用,但是通过封装,以函数的形式提供访问,显得更加整洁,另外,当子类覆盖这些方法的时候,可以对这些数据进行额外的操作修改。




分享到:
评论

相关推荐

    设计模式实现——观察者模式

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于实现发布-订阅...

    java 设计模式 观察者模式 简单实例 包括测试test类

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于实现事件驱动或者发布...

    设计模式-观察者

    5. **应用实例**:观察者模式广泛应用于各种场景,如事件驱动编程、GUI组件通信、消息队列等。例如,当用户在网页上点击按钮时,按钮作为被观察者,会触发一系列事件,这些事件的监听器(观察者)会接收到通知并执行...

    观察者模式模版和例子

    在观察者模式中,有三个关键角色: 1. **主题(Subject)**:它是被观察的对象,可以是任何类型的对象,具有增加、删除观察者以及通知观察者更新状态的能力。 2. **观察者(Observer)**:关注主题状态变化的对象,...

    观察者模式练习

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于事件驱动的系统或者...

    Java内置观察者模式

    观察者模式(Observer Pattern)是设计模式中的一种行为模式,它允许一个对象,当其状态发生改变时,能够自动通知所有依赖它的其他对象。在Java中,这种模式已经被内置到语言核心,使得开发者可以轻松地实现事件驱动...

    设计模式之观察者模式

    在观察者模式中,我们有两个核心的角色:主题(Subject)和观察者(Observer)。主题是被观察的对象,它可以是任何具有可变状态的实体;而观察者是对主题状态变化感兴趣的对象,它们注册到主题上,以便在主题状态...

    设计模式之观察者模式(Observer)

    观察者模式(Observer)是软件设计模式中的一种行为模式,其主要目的是在对象之间建立一种松散耦合的关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式广泛应用于事件驱动...

    观察者模式的一个应用

    ### 观察者模式概述与应用 #### 一、观察者模式定义 观察者模式是一种常用的软件设计模式,主要用于处理对象间的依赖关系。...在实际开发中,观察者模式经常被用于构建动态的、灵活的事件处理系统和消息通知系统。

    设计模式:观察者模式java和javabean

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式常用于实现事件驱动的编程...

    观察者模式源码

    观察者模式(Observer Pattern)是软件设计模式的一种...例如,GUI组件中的按钮点击事件处理、订阅发布系统、数据绑定框架等都是观察者模式的典型应用。理解并熟练运用观察者模式,有助于提升软件的可维护性和扩展性。

    观察者模式代码

    在标准的观察者模式中,观察者并不关心目标的内部状态,只知道目标发生了变化。 3. **具体目标(Concrete Subject)**:实现了目标接口的具体类,它在状态改变时会通知所有观察者。 4. **具体观察者(Concrete ...

    观察者模式 c++ 实现

    观察者模式在C++中的应用非常广泛,例如GUI编程中的事件处理、多线程通信、日志系统等。它提供了一种松耦合的方式,使得主题和观察者之间可以独立地进行扩展和修改,增强了系统的灵活性和可维护性。

    观察者模式使用

    在观察者模式中,有三个关键角色: 1. **主题(Subject)**:这是被观察的对象,它可以是抽象的或具体的。主题维护一个观察者列表,并提供添加、删除观察者的方法,以及通知所有观察者的机制。 2. **观察者...

    观察者模式案例学习代码

    在观察者模式中,有三个关键角色: 1. 主题(Subject):它是被观察的对象,可以是抽象类或接口,维护一个观察者列表,并提供添加、删除观察者和通知观察者的方法。 2. 观察者(Observer):它定义了更新接口,当...

    C++观察者模式学习代码

    在描述中提到的任务队列,可能是在观察者模式中作为具体主题的一部分,用来管理需要执行的任务。任务队列可以通过插入新任务、移除任务或更新任务状态来触发观察者的更新。例如,当任务队列中的任务完成时,可以通知...

    观察者模式java实现观察者设计模式

    观察者模式(Observer Pattern)是软件设计模式中的一种行为模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在Java中,我们可以利用Java提供的...

Global site tag (gtag.js) - Google Analytics