import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
/**
* 《重构,改善现有代码的设计》第八章 Duplicate Observed Data
*
* 业务逻辑:
* 一个JFrame(类IntervalWindow)有三个TextField,分别是start,end和length
* 如果你修改Start或End,length就会自动成为两者计算所得的长度
* 如果你修改length,End就会随之改变(start不变)
*
* 主要是使用了Observer模式:
* start,end和length之间的运算,是与具体界面无关的,应该独立出来(成为一个Subject,观察者模式里面的“目标”)
*
* 关键在于解决以下两个问题:
* 1.什么时候计算?当界面上的TextField值有变动时,由WindowObserver调用Subject的set方法
* 2.计算后如何通知界面?Subject调用notifyObservers方法,这个方法会触发WindowObserver的update方法
*
*/
public class ObserveGUIWindow {
public static void main(String[] args) {
new WindowObserver().init();
}
}
class WindowObserver implements Observer {
private TextField startField;
private TextField endField;
private TextField lengthField;
private Subject subject;
public void init() {
JFrame f = new JFrame("This is a test");
f.setSize(200, 200);
Container content = f.getContentPane();
content.setBackground(Color.white);
content.setLayout(new FlowLayout());
startField = new TextField("0", 10);
endField = new TextField("0", 10);
lengthField = new TextField("0", 10);
SymFocus listener = new SymFocus();
startField.addFocusListener(listener);
endField.addFocusListener(listener);
lengthField.addFocusListener(listener);
Label startLabel = new Label("start:");
Label endLabel = new Label("end:");
Label lengthLabel = new Label("length:");
content.add(startLabel);
content.add(startField);
content.add(endLabel);
content.add(endField);
content.add(lengthLabel);
content.add(lengthField);
f.setLocationRelativeTo(null);
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
subject = new Subject();
subject.addObserver(this);
}
//更新界面,最新的值(计算后)来自Subject
public void update(Observable o, Object arg) {
Subject subject = (Subject)o;
startField.setText(subject.getStart());
endField.setText(subject.getEnd());
lengthField.setText(subject.getLength());
}
class SymFocus extends FocusAdapter {
public void focusLost(FocusEvent event) {
Object object = event.getSource();
if (object == startField)
StartFieldFocusLost(event);
else if (object == endField)
EndFieldFocusLost(event);
else if (object == lengthField)
LengthFieldFocusLost(event);
}
}
void StartFieldFocusLost(FocusEvent event) {
//从界面取得输入的值
String start = startField.getText();
subject.setStart(isInteger(start) ? start : "0");
//交由Subject计算
subject.calculateLength();
}
void EndFieldFocusLost(FocusEvent event) {
String end = endField.getText();
subject.setEnd(isInteger(end) ? end : "0");
subject.calculateLength();
}
void LengthFieldFocusLost(FocusEvent event) {
String length = lengthField.getText();
subject.setLength(isInteger(length) ? length : "0");
subject.calculateEnd();
}
boolean isInteger(String str) {
return str != null && str.matches("[0-9]+");
}
}
class Subject extends Observable {
private String start = "0";
private String end = "0";
private String length = "0";
void calculateLength() {
try {
int start = Integer.parseInt(getStart());
int end = Integer.parseInt(getEnd());
int length = end - start;
this.setLength(String.valueOf(length));
} catch (NumberFormatException e) {
throw new RuntimeException("Unexpected Number Format Error");
}
}
void calculateEnd() {
try {
int start = Integer.parseInt(getStart());
int length = Integer.parseInt(getLength());
int end = start + length;
this.setEnd(String.valueOf(end));
} catch (NumberFormatException e) {
throw new RuntimeException("Unexpected Number Format Error");
}
}
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
//下面这两个方法在setter里面可以不调用,改到calculateEnd和calculateLength再调用更好
this.setChanged();
this.notifyObservers();
}
public String getEnd() {
return end;
}
public void setEnd(String end) {
this.end = end;
this.setChanged();
this.notifyObservers();
}
public String getLength() {
return length;
}
public void setLength(String length) {
this.length = length;
this.setChanged();
this.notifyObservers();
}
}
分享到:
相关推荐
第8章 重新组织数据 169 8.1 Self Encapsulate Field(自封装字段) 171 8.2 Replace Data Value with Object(以对象取代数据值) 175 8.3 Change Value to Reference(将值对象改为引用对象) 179 8.4 ...
第8章 重新组织数据 8.1 Self Encapsulate Field(自封装字段) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将值对象改为引用对象) 8.4 Change Reference to Value(将...
第8章 重新组织你的数据 8.1 Self Encapsulate Field(自封装值域) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将实值对象改为引用对象) 8.4 Change Reference to ...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
### 重构——改善既有代码的设计 #### 知识点概览 《重构——改善既有代码的设计》是一本针对软件开发人员提升代码质量的经典著作。本书不仅深入浅出地讲解了重构的基本概念、方法论,还提供了丰富的重构示例,帮助...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
《重构:改善既有代码的设计》是一本关于如何改进现有代码结构、提高代码可维护性的经典著作。本书由Martin Fowler撰写,详细介绍了各种重构技巧和模式,并通过实际示例展示了如何在不改变软件外部行为的情况下优化...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
### 重构——改善既有代码的设计 #### 知识点概览 **重构**是指在不改变软件外部行为的前提下,对代码进行结构上的调整,以提高其可读性、可维护性和扩展性。这一过程通常涉及一系列经过验证的技术,称为重构模式...
### 重构:改善既有代码的设计 #### 知识点概览 重构是软件开发中一个重要的环节,旨在改进现有代码的结构而不改变其外部行为。本文档基于《重构—改善既有代码的设计》这本书的部分内容,提供了多种重构技术的概述...