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(); } }