Duplicate Observed Data
在翻看《重构-改善既有代码的设计》这本经典的书,书中就介绍了一个重构方法--Duplicate Observed Data 复制被监视数据的重构方法,使用这种方法能够使界面和对数据的操作隔离,去高度耦合。这样方便平台移植。
网上也有这个方法的介绍,大多在抄书,抄写其中的文字,给出的代码也不是一个完整工程,我试着写出整个工程,整理出重构前和重构后的代码。
重构前的完整例子是这样的,尽量保持与书中代码一致。
package nelson.io; import java.awt.Frame;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.Box; public class MainFrame{ private Frame f = new Frame("测试"); private TextField beginField = new TextField("");
private TextField endField = new TextField("");
private TextField lengthField = new TextField("");
private Label beginLabel = new Label("Start:");
private Label endLabel = new Label("End:");
private Label lengthLabel = new Label("Length:"); //定义水平摆放组件的Box对象
private Box horizontal1 = Box.createHorizontalBox();
private Box horizontal2 = Box.createHorizontalBox();
private Box horizontal3 = Box.createHorizontalBox();
private Box vertical1 = Box.createVerticalBox(); public static void main(String [] args)
{
new MainFrame().init();
} class SymFocus extends java.awt.event.FocusAdapter
{
public void focusLost(FocusEvent e)
{
Object obj = e.getSource();
if(obj == beginField)
{
beginField_lostFocus(e);
}
else if(obj == endField)
{
endField_lostFocus(e);
}
else if(obj == lengthField)
{
lengthField_lostFocus(e);
}
}
} private boolean isNotInteger(String strNum)
{
try
{
Integer.parseInt(strNum);
return false;
}
catch (NumberFormatException e)
{
return true;
}
} public void beginField_lostFocus(FocusEvent e)
{
if(isNotInteger(beginField.getText()))
beginField.setText("0"); calculateLength();
} public void endField_lostFocus(FocusEvent e)
{
if(isNotInteger(endField.getText()))
endField.setText("0"); calculateLength();
} public void lengthField_lostFocus(FocusEvent e)
{
if(isNotInteger(lengthField.getText()))
lengthField.setText("0"); calculateEnd();
} /*
* 初始化界面
*/
public void init()
{
beginField.addFocusListener(new SymFocus());
endField.addFocusListener(new SymFocus());
lengthField.addFocusListener(new SymFocus());
horizontal1.add(beginLabel);
horizontal1.add(beginField);
horizontal2.add(endLabel);
horizontal2.add(endField);
horizontal3.add(lengthLabel);
horizontal3.add(lengthField);
vertical1.add(horizontal1);
vertical1.add(horizontal2);
vertical1.add(horizontal3);
f.add(vertical1);
f.pack();
f.setSize(300, 120);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
} /**
* 计算结束的值
*/
private void calculateEnd() {
try{
int begin=Integer.parseInt(this.beginField.getText());
int length=Integer.parseInt(this.lengthField.getText());
int end=length+begin;
this.endField.setText(String.valueOf(end));
}
catch(java.lang.NumberFormatException e)
{
this.beginField.setText(String.valueOf(0));
this.endField.setText(String.valueOf(0));
this.lengthField.setText(String.valueOf(0));
}
} /*
*计算长度的值
*/
private void calculateLength() {
try{
int begin=Integer.parseInt(this.beginField.getText());
int end=Integer.parseInt(this.endField.getText());
int length=end-begin;
this.lengthField.setText(String.valueOf(length));
}
catch(java.lang.NumberFormatException e)
{
this.beginField.setText(String.valueOf(0));
this.endField.setText(String.valueOf(0));
this.lengthField.setText(String.valueOf(0));
}
}
}
程序运行结果如下,大致完善。
下面再给出重构后的代码:
package nelson.io; import java.awt.Frame;
import java.awt.Label;
import java.awt.TextField;
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.Box; public class MainFrame implements Observer{ private Frame f; //控件
private TextField beginField;
private TextField endField;
private TextField lengthField;
private Label beginLabel;
private Label endLabel;
private Label lengthLabel; //定义水平摆放组件的Box对象
private Box horizontal1 = Box.createHorizontalBox();
private Box horizontal2 = Box.createHorizontalBox();
private Box horizontal3 = Box.createHorizontalBox();
private Box vertical1 = Box.createVerticalBox(); //内部模型类对象
private Interval _subject; public static void main(String [] args)
{
new MainFrame().init();
} //构造器
public MainFrame()
{
f = new Frame("测试");
beginLabel = new Label("Start:");
endLabel = new Label("End:");
beginField = new TextField("");
endField = new TextField("");
lengthField = new TextField("");
lengthLabel = new Label("Length:"); _subject = new Interval();
_subject.addObserver(this);
update(_subject,null);
} public void update(Observable o, Object arg)
{
endField.setText(_subject.getEnd());
beginField.setText(_subject.getBegin());
lengthField.setText(_subject.getLength());
} public String getEnd()
{
return _subject.getEnd();
} public void setEnd(String end)
{
_subject.setEnd(end);
} public String getBegin()
{
return _subject.getBegin();
} public void setBegin(String begin)
{
_subject.setBegin(begin);
} public String getLength()
{
return _subject.getLength();
} public void setLength(String length)
{
_subject.setLength(length);
} class SymFocus extends java.awt.event.FocusAdapter
{
public void focusLost(FocusEvent e)
{
Object obj = e.getSource();
if(obj == beginField)
{
beginField_lostFocus(e);
}
else if(obj == endField)
{
endField_lostFocus(e);
}
else if(obj == lengthField)
{
lengthField_lostFocus(e);
}
}
} private boolean isNotInteger(String strNum)
{
try
{
Integer.parseInt(strNum);
return false;
}
catch (NumberFormatException e)
{
return true;
}
} public void beginField_lostFocus(FocusEvent e)
{
if(isNotInteger(beginField.getText()))
setBegin("0");
else
setBegin(beginField.getText());
_subject.calculateLength();
} public void endField_lostFocus(FocusEvent e)
{
if(isNotInteger(endField.getText()))
setEnd("0");
else
setEnd(endField.getText()); _subject.calculateLength();
} public void lengthField_lostFocus(FocusEvent e)
{
if(isNotInteger(lengthField.getText()))
setLength("0");
else
setLength(lengthField.getText());
_subject.calculateEnd();
} /*
* 初始化界面
*/
public void init()
{
beginField.addFocusListener(new SymFocus());
endField.addFocusListener(new SymFocus());
lengthField.addFocusListener(new SymFocus());
horizontal1.add(beginLabel);
horizontal1.add(beginField);
horizontal2.add(endLabel);
horizontal2.add(endField);
horizontal3.add(lengthLabel);
horizontal3.add(lengthField);
vertical1.add(horizontal1);
vertical1.add(horizontal2);
vertical1.add(horizontal3);
f.add(vertical1);
f.pack();
f.setSize(300, 120);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
} class Interval extends Observable {
private String _end = "0";
private String _begin = "0";
private String _length = "0"; public String getEnd() {
return _end;
} public void setEnd(String end) {
_end = end;
setChanged();
notifyObservers();
} public String getBegin() {
return _begin;
} public void setBegin(String begin) {
_begin = begin;
setChanged();
notifyObservers();
} public String getLength() {
return _length;
} public void setLength(String length) {
_length = length;
setChanged();
notifyObservers();
} public void calculateEnd() {
try {
int begin = Integer.parseInt(getBegin());
int length = Integer.parseInt(getLength());
int end = length + begin;
setEnd(String.valueOf(end));
} catch (java.lang.NumberFormatException e) { }
} /*
* 计算长度的值
*/
public void calculateLength() {
try {
int begin = Integer.parseInt(getBegin());
int end = Integer.parseInt(getEnd());
int length = end - begin;
setLength(String.valueOf(length));
} catch (java.lang.NumberFormatException e) { }
}
}
总结一下重构过程:
1、界面中的文本框元素与中间类中的文本数据一一对应,也就是Duplicate Observerd Data。
2、界面类中的数据赋值与取值函数全部委托给中间类,当然对数据计算肯定也是在中间类中完成的,界面类根本不需要知道中间类中计算过程的存在,界面类只复制界面的显示。
3、中间类中数据的更新需要通知界面类,这里使用了Java的Observer模式。相当于界面在中间类中注册了一个回调函数。
上述代码依然可以再次重构,比如中间类Interval名称就应该改为数据模型类MainFramModel(针对MainFrame界面的数据模型model)。另外,文本框内容变动时的响应函数里,在响应函数里做了对输入规范(要求是数据)的判断,其实依然可以交给数据模型类来处理,相对于给数据模型类的元素赋值函数处理时的输入数据校验,这样界面类更简洁更纯粹。另外,文本框内容的变动可能由网络数据更新(或者其他渠道更新),这样数据模型类就应该申明成public型,作为一个单独的文件,与界面类的隔离更彻底。
再次整理后的代码如下:
界面类:
package nelson.io; import java.awt.Frame;
import java.awt.Label;
import java.awt.TextField;
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.Box; public class MainFrame implements Observer{ private Frame f; //控件
private TextField beginField;
private TextField endField;
private TextField lengthField;
private Label beginLabel;
private Label endLabel;
private Label lengthLabel; //定义水平摆放组件的Box对象
private Box horizontal1 = Box.createHorizontalBox();
private Box horizontal2 = Box.createHorizontalBox();
private Box horizontal3 = Box.createHorizontalBox();
private Box vertical1 = Box.createVerticalBox(); private MainFrameModel _datamodel; //对应界面的数据模型 //构造器
public MainFrame()
{
f = new Frame("测试");
beginLabel = new Label("Start:");
endLabel = new Label("End:");
beginField = new TextField("");
endField = new TextField("");
lengthField = new TextField("");
lengthLabel = new Label("Length:"); _datamodel = new MainFrameModel();
_datamodel.addObserver(this);
update(_datamodel,null);
} public void update(Observable o, Object arg)
{
endField.setText(_datamodel.getEnd());
beginField.setText(_datamodel.getBegin());
lengthField.setText(_datamodel.getLength());
} public static void main(String [] args)
{
new MainFrame().init();
} public String getEnd()
{
return _datamodel.getEnd();
} public void setEnd(String end)
{
_datamodel.setEnd(end);
} public String getBegin()
{
return _datamodel.getBegin();
} public void setBegin(String begin)
{
_datamodel.setBegin(begin);
} public String getLength()
{
return _datamodel.getLength();
} public void setLength(String length)
{
_datamodel.setLength(length);
} private void calculateLength()
{
_datamodel.calculateLength();
} private void calculateEnd()
{
_datamodel.calculateEnd();
} class SymFocus extends java.awt.event.FocusAdapter
{
public void focusLost(FocusEvent e)
{
Object obj = e.getSource();
if(obj == beginField)
{
setBegin(beginField.getText());
calculateLength();
}
else if(obj == endField)
{
setEnd(endField.getText());
calculateLength();
}
else if(obj == lengthField)
{
setLength(lengthField.getText());
calculateEnd();
}
}
} /*
* 初始化界面
*/
public void init()
{
beginField.addFocusListener(new SymFocus());
endField.addFocusListener(new SymFocus());
lengthField.addFocusListener(new SymFocus());
horizontal1.add(beginLabel);
horizontal1.add(beginField);
horizontal2.add(endLabel);
horizontal2.add(endField);
horizontal3.add(lengthLabel);
horizontal3.add(lengthField);
vertical1.add(horizontal1);
vertical1.add(horizontal2);
vertical1.add(horizontal3);
f.add(vertical1);
f.pack();
f.setSize(300, 120);
f.setVisible(true);
f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
数据模型类:
package nelson.io; import java.util.Observable; public class MainFrameModel extends Observable{ private String _end = "0";
private String _begin = "0";
private String _length = "0"; public MainFrameModel()
{ } public String getEnd() {
return _end;
} public void setEnd(String end) {
int input=0;
try
{
input = Integer.parseInt(end);
}
catch(NumberFormatException e)
{
input = 0;
}
_end = input+"";
setChanged();
notifyObservers();
} public String getBegin() {
return _begin;
} public void setBegin(String begin) {
int input=0;
try
{
input = Integer.parseInt(begin);
}
catch(NumberFormatException e)
{
input = 0;
}
_begin = input+"";
setChanged();
notifyObservers();
} public String getLength() {
return _length;
} public void setLength(String length) {
int input=0;
try
{
input = Integer.parseInt(length);
}
catch(NumberFormatException e)
{
input = 0;
}
_length = input+"";
setChanged();
notifyObservers();
} public void calculateEnd() {
int begin = Integer.parseInt(getBegin());
int length = Integer.parseInt(getLength());
int end = length + begin;
setEnd(String.valueOf(end));
} public void calculateLength() {
int begin = Integer.parseInt(getBegin());
int end = Integer.parseInt(getEnd());
int length = end - begin;
setLength(String.valueOf(length));
}
}
整理完毕。
Duplicate Observed Data的更多相关文章
- 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data
当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...
- ORACLE 11g 用Duplicate恢复Data Guard 备库详细过程
1.先查找备库控制文件路径 先在备库上找出控制文件的路径,通过和主库一样,不过为了以防万一,还是check为好. SQL> select name from v$controlfile; NA ...
- [转] Agile Software Development 敏捷软件开发
原文作者:kkun 原文地址:http://www.cnblogs.com/kkun/archive/2011/07/06/agile_software_development.html 敏捷是什么 ...
- 代码的坏味道(2)——过大的类(Large Class)
坏味道--过大的类(Large Class) 特征 一个类含有过多字段.函数.代码行. 问题原因 类通常一开始很小,但是随着程序的增长而逐渐膨胀. 类似于过长函数,程序员通常觉得在一个现存类中添加新特 ...
- C#重构之道
定义 重构的定义:在不改变软件可观察行为的前提下,改善其内部结构. 其中,不改变软件行为,是重构最基本的要求.要想真正发挥威力,就必须做到“不需了解软件行为”. 如果一段代码能让你容易了解其行为,说明 ...
- Java中有四种常见的Map实现方法
在 HTML5 之前我们做图片预览主流做法有两种,第一种是通过 Flash 插件来做预览,第二种是 Ajax 实现的假预览,也就是说选择图片文件后,图片其实已经异步上传到服务器,服务器处理后返回图片路 ...
- 敏捷软件开发 Agile software Development(转)
原文链接: http://www.cnblogs.com/kkun/archive/2011/07/06/2099253.html 敏捷软件开发 Agile software Development ...
- 《重构——改善既有代码的设计》【PDF】下载
<重构--改善既有代码的设计>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196358 编辑推荐 重构,一言以蔽之,就是在不改变外 ...
- BookNote: Refactoring - Improving the Design of Existing Code
BookNote: Refactoring - Improving the Design of Existing Code From "Refactoring - Improving the ...
随机推荐
- CF dp 一句话解题
wyq说刚入门oi 或是遇到瓶颈的时候就刷DP吧,虽然觉得这么刷CF题有点浪费,但是还是挺爽的,按照solved排序做的,前面的题都挺水的(忘记记录了混蛋),就不写了,从5C开始写解题 CF5 C. ...
- 刷题总结——保留道路(ssoj)
题目: 题目背景 161114-练习-DAY1-AHSDFZ T3 题目描述 很久很久以前有一个国家,这个国家有 N 个城市,城市由 1,2,3,…,,N 标号,城市间有 M 条双向道路,每条道路都有 ...
- [SDOI2008]仪仗队 (欧拉函数)
题目描述 作为体育委员,C君负责这次运动会仪仗队的训练.仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图 ...
- PHP中的验证码类(准备篇)
<!--code.php内容--> <?php //开启session session_start(); include "vcode.class.php"; / ...
- android开发里跳过的坑——TimePickerDialog onTimeSet不回调
在android6.0.1上测试发现TimePickerDialog的onTimeSet和DatePickerDialog的onDateSet不回调,查看SDK源码发现,TimePickerDialo ...
- [NOIP2011] 洛谷P1313 计算系数
题目描述 给定一个多项式(by+ax)^k,请求出多项式展开后x^n*y^m 项的系数. 输入输出格式 输入格式: 输入文件名为factor.in. 共一行,包含5 个整数,分别为 a ,b ,k , ...
- 小程序-列表块/类式ul-li格式(1)
摘要 目前列表能布局出来,但是目前我个人还没解决的问题是:如果每个列表块都有详情页怎么解决呢? 1:我的效果图 2.正常的每个都能点击的html 注:上面的代码确实能够实现我的每个[menu2_vie ...
- eslint 在webstorm配置
1.安装nodejs和eslint 2.在 webstorm 的 file - setting搜索eslint,配置eslint路径 3.在项目目录下新建.eslintrc文件 4.配置eslint ...
- Keil建立第一个C51工程的步骤
参见51+arm开发板<使用手册.pdf> 1.“project” >> “new project” >> 新建一个用于保存工程的文件夹例如dem &g ...
- [ZJOI 2018] 线图
别想多了我怎么可能会正解呢2333,我只会30分暴力(好像现场拿30分已经不算少了2333,虽然我局的30分不是特别难想). 首先求k次转化的点数显然可以变成求k-1次转化之后的边数,所以我们可以先让 ...