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章 重新组织你的数据 8.1 Self Encapsulate Field(自封装值域) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将实值对象改为引用对象) 8.4 Change Reference to ...
第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 ...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
第1 章重构,第一个案例···· · ·· · · ····· · · ····· ··· · ····· · · ·….... ……................ ….............. …..... …...... …l I.I 起点···· ·······...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...
第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(将...
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 将双向关联改为单向 *...
Duplicate Observed Data 重复被观察数据 *Change Unidirectional Associationto Bidirectional 将单向关联改为双向 Change Bidirectional Association to Unidirectional 将双向关联改为单向 *...