一个GUI类,包含了业务处理代码和用户界面代码。当业务逻辑的增加, 会让这个类越来越大,而且逻辑会复杂。这是因为,一个GUI类同时承担了两种责任,业务处理功能和界面渲染功能。这时候,就需要把业务处理代码从GUI类中分离出来。
业务处理代码和界面显示代码分离开之后,方便维护和方便开发。在进行业务处理时,需要使用到界面数据,而界面进行更新的时候,也会需要使用到业务处理的结果。这时,就需要数据在业务层和界面层之间进行同步。
public class IntervalWindow extends Frame...
java.awt.TextField _startField;
java.awt.TextField _endField;
java.awt.TextField _lengthField;
class SymFocus extends java.awt.event.FocusAdapter
{
public void focusLost(java.awt.event.FocusEvent event)
{
Object object = event.getSource();
if (object == _startField)
StartField_FocusLost(event);
else if (object == _endField)
EndField_FocusLost(event);
else if (object == _lengthField)
LengthField_FocusLost(event);
}
}
一个GUI界面,有三个文本框,当某个文本框输入内容,离开时,也就是失去焦点的时候,会触发一个计算。
Start文本框失去焦点的时候,执行StartField_FocustLost方法;
End文本框失去焦点的时候,执行EndField_FocusLost方法;
Length文本框失去焦点的时候,执行LengthField_FocusLost方法。
Start文本框和End文本框,失去焦点时,做的事情是,检查文本框的输入是否是整数,如果不是,则将文本框的内容设置为0;然后,计算Length文本框的内容。
Length文本框失去焦点的时候,做的事情是,检查文本框的输入是否是整数,如果不是,则将文本框的内容设置为0;然后,计算当前文本框的值。
这是界面操作代码。
void StartField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_startField.getText()))
_startField.setText("0");
calculateLength();
}
void EndField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_endField.getText()))
_endField.setText("0");
calculateLength();
}
void LengthField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_lengthField.getText()))
_lengthField.setText("0");
calculateEnd();
}
void calculateLength(){
try {
int start = Integer.parseInt(_startField.getText());
int end = Integer.parseInt(_endField.getText());
int length = end - start;
_lengthField.setText(String.valueOf(length));
} catch (NumberFormatException e) {
throw new RuntimeException ("Unexpected Number Format Error");
}
}
void calculateEnd() {
try {
int start = Integer.parseInt(_startField.getText());
int length = Integer.parseInt(_lengthField.getText());
int end = start + length;
_endField.setText(String.valueOf(end));
} catch (NumberFormatException e) {
throw new RuntimeException ("Unexpected Number Format Error");
}
}
calculateLength()方法和calculateEnd()方法,它包含了计算的逻辑,还包含了对界面组件的引用。我们现在想做的事情,就是,让这两个方法与界面组件(_startField,_endField,_lengthField)解耦。
这里就可以使用到Duplicate Observed Data手法。创建一个领域类,领域类和界面类的数据同步;领域类的数据发生更新后,会将结果传递给界面类。
-------------------------------------------------------------------------------------------------------
应用Duplicate Observed Data手法之后,得到如下代码:
整个流程就变成这样:
1.GUI类,IntervalWindow初始化时,使用的是Interval这个类的值。
2.当GUI类发生事件变化的时候,把组件的值传递给_subject实例,并进行计算。
3._subject计算完毕,再通知GUI类界面进行更新。
在这里,计算的逻辑,被分割到_subject实例(领域类)。
数据的传递流程:
领域类,界面初始值数据------>{界面生成(GUI类)------>领域类------>界面}------>{界面生成(GUI类)--->领域类--->界面}
public class IntervalWindow extends Frame implements Observer{
private Interval _subject;
public IntervalWindow(){
_subject = new Interval();
_subject.addObserver(this);
update(_subject, null);
}
String getEnd() {
return _subject.getEnd();
}
void setEnd (String arg) {
_subject.setEnd(arg);
}
void StartField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_startField.getText()))
_startField.setText("0");
_subject.setStart(_startField.getText());
_subject.calculateLength();
}
void EndField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_endField.getText()))
_endField.setText("0");
_subject.setEnd (_endField.getText());
_subject.calculateLength();
}
void LengthField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_lengthField.getText()))
_lengthField.setText("0");
_subject.setLength(_lengthField.getText());
_subject.calculateEnd();
}
@Override
public void update(Observable observed, Object arg) {
_endField.setText(_subject.getEnd());
_startField.setText(_subject.getStart());
_lengthField.setText(_subject.getLength());
}
}
-------------------------------
class Interval extends Observable {
private String _end = "0";
private String _start = "0";
private String _length ="0";
String getEnd() {
return _end;
}
void setEnd (String arg) {
_end = arg;
setChanged();
notifyObservers();
}
String getStart(){
return _start;
}
void setStart(String arg){
_start = arg;
setChanged();
notifyObservers();
}
String getLength(){
return _length;
}
void setLength(String arg){
_length = arg;
setChanged();
notifyObservers();
}
void calculateLength(){
try {
int start = Integer.parseInt(getStart());
int end = Integer.parseInt(getEnd());
int length = end - start;
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;
setEnd(String.valueOf(end));
} catch (NumberFormatException e) {
throw new RuntimeException ("Unexpected Number Format Error");
}
}
}
参考资料:
https://sourcemaking.com/refactoring/duplicate-observed-data
https://lostechies.com/seanchambers/2009/08/20/refactoring-day-20-extract-subclass/
http://www.refactoring.com/catalog/duplicateObservedData.html
http://www.refactoring.com/catalog/extractSubclass.html