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 ...
随机推荐
- 【Luogu】P1040加分二叉树(区间DP)
题目链接 区间DP,因为中序遍历的性质:区间[l,r]的任何一个数都可以是该区间的根节点. 更新权值的时候记录区间的根节点,最后DFS输出. 见代码. #include<cstdio> # ...
- 【DFS序+树状数组】BNUOJ 52733 Random Numbers
http://acm.bnu.edu.cn/v3/problem_show.php?pid=52733 [题意] 给定一棵树,这棵树每个点都有一个点权,标号从0开始,0是根结点 修改操作: SEED ...
- ShareSDK中微信分享错误总结
项目中用到微信分享,可向好友或朋友圈分享链接时,分享人可以打开网站,查看消息者却始终不能打开网站.试了N种方法,重写了N次分享模块,均没办法解决. 在无意中查看分享链接时发现,朋友圈里分享后,原始链接 ...
- 第一行代码 Android 思维导图
第一行代码 Android 思维导图
- [Inside HotSpot] Serial垃圾回收器 (二) Minor GC
Serial垃圾回收器Minor GC 1. DefNewGeneration垃圾回收 新生代使用复制算法做垃圾回收,比老年代的标记-压缩简单很多,所有回收代码都位于DefNewGeneration: ...
- 读取编码器信息Python2.7和Python3.3版本差异及解决,一次订阅多次调用callback的解决
1. Python3.3以字节类型返回编码器信息,b'...',BUF: b'\xc3KOO\x00OO\x00OO\x00OO\x00OO\x00\x03\x00\x00\x00\x00\x99R\ ...
- windows安装RabbitMQ注意事项
1.首先下载好ERLANG.RabbitMQ安装包,先安装erlang,设置好环境变量,然后再去安装MQ; 2.别人有两个报错: 一:RabbitMQ安装目录中不允许有空格; 二:安装rabbitmq ...
- 框架-数据库定义MD5加密
1.--定义Md5加密declare @pt_pwd varchar(50)set @pt_pwd = ''set @pt_pwd = substring(sys.fn_sqlvarbasetostr ...
- java消息队列怎么用
消息队列的使用场景是怎样的? 经常听到诸如rebbitmq,activemq,请教一下各位前辈消息队列的使用场景是怎样的,什么时候会用到它 校验用户名等信息,如果没问题会在数据库中添加一个用户记录 ...
- Tomcat服务器解析“GET /JavaWebDemo1/1.jsp HTTP/1.1”
(2)服务器收到http请求报文,返回http响应报文 Tomcat服务器解析“GET /JavaWebDemo1/1.jsp HTTP/1.1” Tomcat服务器解析“GET /JavaWebDe ...