在翻看《重构-改善既有代码的设计》这本经典的书,书中就介绍了一个重构方法--Duplicate Observed Data 复制被监视数据的重构方法,使用这种方法能够使界面和对数据的操作隔离,去高度耦合。这样方便平台移植。

  网上也有这个方法的介绍,大多在抄书,抄写其中的文字,给出的代码也不是一个完整工程,我试着写出整个工程,整理出重构前和重构后的代码。

重构前的完整例子是这样的,尽量保持与书中代码一致。

  1. package nelson.io;
  2.  
  3. import java.awt.Frame;
  4. import java.awt.Label;
  5. import java.awt.TextField;
  6. import java.awt.event.ActionEvent;
  7. import java.awt.event.ActionListener;
  8. import java.awt.event.FocusEvent;
  9. import java.awt.event.FocusListener;
  10. import java.awt.event.TextEvent;
  11. import java.awt.event.TextListener;
  12. import java.awt.event.WindowAdapter;
  13. import java.awt.event.WindowEvent;
  14. import javax.swing.Box;
  15.  
  16. public class MainFrame{
  17.  
  18. private Frame f = new Frame("测试");
  19.  
  20. private TextField beginField = new TextField("");
  21. private TextField endField = new TextField("");
  22. private TextField lengthField = new TextField("");
  23. private Label beginLabel = new Label("Start:");
  24. private Label endLabel = new Label("End:");
  25. private Label lengthLabel = new Label("Length:");
  26.  
  27. //定义水平摆放组件的Box对象
  28. private Box horizontal1 = Box.createHorizontalBox();
  29. private Box horizontal2 = Box.createHorizontalBox();
  30. private Box horizontal3 = Box.createHorizontalBox();
  31. private Box vertical1 = Box.createVerticalBox();
  32.  
  33. public static void main(String [] args)
  34. {
  35. new MainFrame().init();
  36. }
  37.  
  38. class SymFocus extends java.awt.event.FocusAdapter
  39. {
  40. public void focusLost(FocusEvent e)
  41. {
  42. Object obj = e.getSource();
  43. if(obj == beginField)
  44. {
  45. beginField_lostFocus(e);
  46. }
  47. else if(obj == endField)
  48. {
  49. endField_lostFocus(e);
  50. }
  51. else if(obj == lengthField)
  52. {
  53. lengthField_lostFocus(e);
  54. }
  55. }
  56. }
  57.  
  58. private boolean isNotInteger(String strNum)
  59. {
  60. try
  61. {
  62. Integer.parseInt(strNum);
  63. return false;
  64. }
  65. catch (NumberFormatException e)
  66. {
  67. return true;
  68. }
  69. }
  70.  
  71. public void beginField_lostFocus(FocusEvent e)
  72. {
  73. if(isNotInteger(beginField.getText()))
  74. beginField.setText("0");
  75.  
  76. calculateLength();
  77. }
  78.  
  79. public void endField_lostFocus(FocusEvent e)
  80. {
  81. if(isNotInteger(endField.getText()))
  82. endField.setText("0");
  83.  
  84. calculateLength();
  85. }
  86.  
  87. public void lengthField_lostFocus(FocusEvent e)
  88. {
  89. if(isNotInteger(lengthField.getText()))
  90. lengthField.setText("0");
  91.  
  92. calculateEnd();
  93. }
  94.  
  95. /*
  96. * 初始化界面
  97. */
  98. public void init()
  99. {
  100. beginField.addFocusListener(new SymFocus());
  101. endField.addFocusListener(new SymFocus());
  102. lengthField.addFocusListener(new SymFocus());
  103. horizontal1.add(beginLabel);
  104. horizontal1.add(beginField);
  105. horizontal2.add(endLabel);
  106. horizontal2.add(endField);
  107. horizontal3.add(lengthLabel);
  108. horizontal3.add(lengthField);
  109. vertical1.add(horizontal1);
  110. vertical1.add(horizontal2);
  111. vertical1.add(horizontal3);
  112. f.add(vertical1);
  113. f.pack();
  114. f.setSize(300, 120);
  115. f.setVisible(true);
  116. f.addWindowListener(new WindowAdapter(){
  117.  
  118. public void windowClosing(WindowEvent e)
  119. {
  120. System.exit(0);
  121. }
  122. });
  123. }
  124.  
  125. /**
  126. * 计算结束的值
  127. */
  128. private void calculateEnd() {
  129. try{
  130. int begin=Integer.parseInt(this.beginField.getText());
  131. int length=Integer.parseInt(this.lengthField.getText());
  132. int end=length+begin;
  133. this.endField.setText(String.valueOf(end));
  134. }
  135. catch(java.lang.NumberFormatException e)
  136. {
  137. this.beginField.setText(String.valueOf(0));
  138. this.endField.setText(String.valueOf(0));
  139. this.lengthField.setText(String.valueOf(0));
  140. }
  141. }
  142.  
  143. /*
  144. *计算长度的值
  145. */
  146. private void calculateLength() {
  147. try{
  148. int begin=Integer.parseInt(this.beginField.getText());
  149. int end=Integer.parseInt(this.endField.getText());
  150. int length=end-begin;
  151. this.lengthField.setText(String.valueOf(length));
  152. }
  153. catch(java.lang.NumberFormatException e)
  154. {
  155. this.beginField.setText(String.valueOf(0));
  156. this.endField.setText(String.valueOf(0));
  157. this.lengthField.setText(String.valueOf(0));
  158. }
  159. }
  160. }

  程序运行结果如下,大致完善。

  下面再给出重构后的代码:

  1. package nelson.io;
  2.  
  3. import java.awt.Frame;
  4. import java.awt.Label;
  5. import java.awt.TextField;
  6. import java.awt.event.FocusEvent;
  7. import java.awt.event.WindowAdapter;
  8. import java.awt.event.WindowEvent;
  9. import java.util.Observable;
  10. import java.util.Observer;
  11. import javax.swing.Box;
  12.  
  13. public class MainFrame implements Observer{
  14.  
  15. private Frame f;
  16.  
  17. //控件
  18. private TextField beginField;
  19. private TextField endField;
  20. private TextField lengthField;
  21. private Label beginLabel;
  22. private Label endLabel;
  23. private Label lengthLabel;
  24.  
  25. //定义水平摆放组件的Box对象
  26. private Box horizontal1 = Box.createHorizontalBox();
  27. private Box horizontal2 = Box.createHorizontalBox();
  28. private Box horizontal3 = Box.createHorizontalBox();
  29. private Box vertical1 = Box.createVerticalBox();
  30.  
  31. //内部模型类对象
  32. private Interval _subject;
  33.  
  34. public static void main(String [] args)
  35. {
  36. new MainFrame().init();
  37. }
  38.  
  39. //构造器
  40. public MainFrame()
  41. {
  42. f = new Frame("测试");
  43. beginLabel = new Label("Start:");
  44. endLabel = new Label("End:");
  45. beginField = new TextField("");
  46. endField = new TextField("");
  47. lengthField = new TextField("");
  48. lengthLabel = new Label("Length:");
  49.  
  50. _subject = new Interval();
  51. _subject.addObserver(this);
  52. update(_subject,null);
  53. }
  54.  
  55. public void update(Observable o, Object arg)
  56. {
  57. endField.setText(_subject.getEnd());
  58. beginField.setText(_subject.getBegin());
  59. lengthField.setText(_subject.getLength());
  60. }
  61.  
  62. public String getEnd()
  63. {
  64. return _subject.getEnd();
  65. }
  66.  
  67. public void setEnd(String end)
  68. {
  69. _subject.setEnd(end);
  70. }
  71.  
  72. public String getBegin()
  73. {
  74. return _subject.getBegin();
  75. }
  76.  
  77. public void setBegin(String begin)
  78. {
  79. _subject.setBegin(begin);
  80. }
  81.  
  82. public String getLength()
  83. {
  84. return _subject.getLength();
  85. }
  86.  
  87. public void setLength(String length)
  88. {
  89. _subject.setLength(length);
  90. }
  91.  
  92. class SymFocus extends java.awt.event.FocusAdapter
  93. {
  94. public void focusLost(FocusEvent e)
  95. {
  96. Object obj = e.getSource();
  97. if(obj == beginField)
  98. {
  99. beginField_lostFocus(e);
  100. }
  101. else if(obj == endField)
  102. {
  103. endField_lostFocus(e);
  104. }
  105. else if(obj == lengthField)
  106. {
  107. lengthField_lostFocus(e);
  108. }
  109. }
  110. }
  111.  
  112. private boolean isNotInteger(String strNum)
  113. {
  114. try
  115. {
  116. Integer.parseInt(strNum);
  117. return false;
  118. }
  119. catch (NumberFormatException e)
  120. {
  121. return true;
  122. }
  123. }
  124.  
  125. public void beginField_lostFocus(FocusEvent e)
  126. {
  127. if(isNotInteger(beginField.getText()))
  128. setBegin("0");
  129. else
  130. setBegin(beginField.getText());
  131. _subject.calculateLength();
  132. }
  133.  
  134. public void endField_lostFocus(FocusEvent e)
  135. {
  136. if(isNotInteger(endField.getText()))
  137. setEnd("0");
  138. else
  139. setEnd(endField.getText());
  140.  
  141. _subject.calculateLength();
  142. }
  143.  
  144. public void lengthField_lostFocus(FocusEvent e)
  145. {
  146. if(isNotInteger(lengthField.getText()))
  147. setLength("0");
  148. else
  149. setLength(lengthField.getText());
  150. _subject.calculateEnd();
  151. }
  152.  
  153. /*
  154. * 初始化界面
  155. */
  156. public void init()
  157. {
  158. beginField.addFocusListener(new SymFocus());
  159. endField.addFocusListener(new SymFocus());
  160. lengthField.addFocusListener(new SymFocus());
  161. horizontal1.add(beginLabel);
  162. horizontal1.add(beginField);
  163. horizontal2.add(endLabel);
  164. horizontal2.add(endField);
  165. horizontal3.add(lengthLabel);
  166. horizontal3.add(lengthField);
  167. vertical1.add(horizontal1);
  168. vertical1.add(horizontal2);
  169. vertical1.add(horizontal3);
  170. f.add(vertical1);
  171. f.pack();
  172. f.setSize(300, 120);
  173. f.setVisible(true);
  174. f.addWindowListener(new WindowAdapter(){
  175.  
  176. public void windowClosing(WindowEvent e)
  177. {
  178. System.exit(0);
  179. }
  180. });
  181. }
  182. }
  183.  
  184. class Interval extends Observable {
  185. private String _end = "0";
  186. private String _begin = "0";
  187. private String _length = "0";
  188.  
  189. public String getEnd() {
  190. return _end;
  191. }
  192.  
  193. public void setEnd(String end) {
  194. _end = end;
  195. setChanged();
  196. notifyObservers();
  197. }
  198.  
  199. public String getBegin() {
  200. return _begin;
  201. }
  202.  
  203. public void setBegin(String begin) {
  204. _begin = begin;
  205. setChanged();
  206. notifyObservers();
  207. }
  208.  
  209. public String getLength() {
  210. return _length;
  211. }
  212.  
  213. public void setLength(String length) {
  214. _length = length;
  215. setChanged();
  216. notifyObservers();
  217. }
  218.  
  219. public void calculateEnd() {
  220. try {
  221. int begin = Integer.parseInt(getBegin());
  222. int length = Integer.parseInt(getLength());
  223. int end = length + begin;
  224. setEnd(String.valueOf(end));
  225. } catch (java.lang.NumberFormatException e) {
  226.  
  227. }
  228. }
  229.  
  230. /*
  231. * 计算长度的值
  232. */
  233. public void calculateLength() {
  234. try {
  235. int begin = Integer.parseInt(getBegin());
  236. int end = Integer.parseInt(getEnd());
  237. int length = end - begin;
  238. setLength(String.valueOf(length));
  239. } catch (java.lang.NumberFormatException e) {
  240.  
  241. }
  242. }
  243. }

  总结一下重构过程:

  1、界面中的文本框元素与中间类中的文本数据一一对应,也就是Duplicate Observerd Data。

  2、界面类中的数据赋值与取值函数全部委托给中间类,当然对数据计算肯定也是在中间类中完成的,界面类根本不需要知道中间类中计算过程的存在,界面类只复制界面的显示。

  3、中间类中数据的更新需要通知界面类,这里使用了Java的Observer模式。相当于界面在中间类中注册了一个回调函数。

上述代码依然可以再次重构,比如中间类Interval名称就应该改为数据模型类MainFramModel(针对MainFrame界面的数据模型model)。另外,文本框内容变动时的响应函数里,在响应函数里做了对输入规范(要求是数据)的判断,其实依然可以交给数据模型类来处理,相对于给数据模型类的元素赋值函数处理时的输入数据校验,这样界面类更简洁更纯粹。另外,文本框内容的变动可能由网络数据更新(或者其他渠道更新),这样数据模型类就应该申明成public型,作为一个单独的文件,与界面类的隔离更彻底。

  再次整理后的代码如下:

  界面类:

  1. package nelson.io;
  2.  
  3. import java.awt.Frame;
  4. import java.awt.Label;
  5. import java.awt.TextField;
  6. import java.awt.event.FocusEvent;
  7. import java.awt.event.WindowAdapter;
  8. import java.awt.event.WindowEvent;
  9. import java.util.Observable;
  10. import java.util.Observer;
  11. import javax.swing.Box;
  12.  
  13. public class MainFrame implements Observer{
  14.  
  15. private Frame f;
  16.  
  17. //控件
  18. private TextField beginField;
  19. private TextField endField;
  20. private TextField lengthField;
  21. private Label beginLabel;
  22. private Label endLabel;
  23. private Label lengthLabel;
  24.  
  25. //定义水平摆放组件的Box对象
  26. private Box horizontal1 = Box.createHorizontalBox();
  27. private Box horizontal2 = Box.createHorizontalBox();
  28. private Box horizontal3 = Box.createHorizontalBox();
  29. private Box vertical1 = Box.createVerticalBox();
  30.  
  31. private MainFrameModel _datamodel; //对应界面的数据模型
  32.  
  33. //构造器
  34. public MainFrame()
  35. {
  36. f = new Frame("测试");
  37. beginLabel = new Label("Start:");
  38. endLabel = new Label("End:");
  39. beginField = new TextField("");
  40. endField = new TextField("");
  41. lengthField = new TextField("");
  42. lengthLabel = new Label("Length:");
  43.  
  44. _datamodel = new MainFrameModel();
  45. _datamodel.addObserver(this);
  46. update(_datamodel,null);
  47. }
  48.  
  49. public void update(Observable o, Object arg)
  50. {
  51. endField.setText(_datamodel.getEnd());
  52. beginField.setText(_datamodel.getBegin());
  53. lengthField.setText(_datamodel.getLength());
  54. }
  55.  
  56. public static void main(String [] args)
  57. {
  58. new MainFrame().init();
  59. }
  60.  
  61. public String getEnd()
  62. {
  63. return _datamodel.getEnd();
  64. }
  65.  
  66. public void setEnd(String end)
  67. {
  68. _datamodel.setEnd(end);
  69. }
  70.  
  71. public String getBegin()
  72. {
  73. return _datamodel.getBegin();
  74. }
  75.  
  76. public void setBegin(String begin)
  77. {
  78. _datamodel.setBegin(begin);
  79. }
  80.  
  81. public String getLength()
  82. {
  83. return _datamodel.getLength();
  84. }
  85.  
  86. public void setLength(String length)
  87. {
  88. _datamodel.setLength(length);
  89. }
  90.  
  91. private void calculateLength()
  92. {
  93. _datamodel.calculateLength();
  94. }
  95.  
  96. private void calculateEnd()
  97. {
  98. _datamodel.calculateEnd();
  99. }
  100.  
  101. class SymFocus extends java.awt.event.FocusAdapter
  102. {
  103. public void focusLost(FocusEvent e)
  104. {
  105. Object obj = e.getSource();
  106. if(obj == beginField)
  107. {
  108. setBegin(beginField.getText());
  109. calculateLength();
  110. }
  111. else if(obj == endField)
  112. {
  113. setEnd(endField.getText());
  114. calculateLength();
  115. }
  116. else if(obj == lengthField)
  117. {
  118. setLength(lengthField.getText());
  119. calculateEnd();
  120. }
  121. }
  122. }
  123.  
  124. /*
  125. * 初始化界面
  126. */
  127. public void init()
  128. {
  129. beginField.addFocusListener(new SymFocus());
  130. endField.addFocusListener(new SymFocus());
  131. lengthField.addFocusListener(new SymFocus());
  132. horizontal1.add(beginLabel);
  133. horizontal1.add(beginField);
  134. horizontal2.add(endLabel);
  135. horizontal2.add(endField);
  136. horizontal3.add(lengthLabel);
  137. horizontal3.add(lengthField);
  138. vertical1.add(horizontal1);
  139. vertical1.add(horizontal2);
  140. vertical1.add(horizontal3);
  141. f.add(vertical1);
  142. f.pack();
  143. f.setSize(300, 120);
  144. f.setVisible(true);
  145. f.addWindowListener(new WindowAdapter(){
  146.  
  147. public void windowClosing(WindowEvent e)
  148. {
  149. System.exit(0);
  150. }
  151. });
  152. }
  153. }

  数据模型类:

  1. package nelson.io;
  2.  
  3. import java.util.Observable;
  4.  
  5. public class MainFrameModel extends Observable{
  6.  
  7. private String _end = "0";
  8. private String _begin = "0";
  9. private String _length = "0";
  10.  
  11. public MainFrameModel()
  12. {
  13.  
  14. }
  15.  
  16. public String getEnd() {
  17. return _end;
  18. }
  19.  
  20. public void setEnd(String end) {
  21. int input=0;
  22. try
  23. {
  24. input = Integer.parseInt(end);
  25. }
  26. catch(NumberFormatException e)
  27. {
  28. input = 0;
  29. }
  30. _end = input+"";
  31. setChanged();
  32. notifyObservers();
  33. }
  34.  
  35. public String getBegin() {
  36. return _begin;
  37. }
  38.  
  39. public void setBegin(String begin) {
  40. int input=0;
  41. try
  42. {
  43. input = Integer.parseInt(begin);
  44. }
  45. catch(NumberFormatException e)
  46. {
  47. input = 0;
  48. }
  49. _begin = input+"";
  50. setChanged();
  51. notifyObservers();
  52. }
  53.  
  54. public String getLength() {
  55. return _length;
  56. }
  57.  
  58. public void setLength(String length) {
  59. int input=0;
  60. try
  61. {
  62. input = Integer.parseInt(length);
  63. }
  64. catch(NumberFormatException e)
  65. {
  66. input = 0;
  67. }
  68. _length = input+"";
  69. setChanged();
  70. notifyObservers();
  71. }
  72.  
  73. public void calculateEnd() {
  74. int begin = Integer.parseInt(getBegin());
  75. int length = Integer.parseInt(getLength());
  76. int end = length + begin;
  77. setEnd(String.valueOf(end));
  78. }
  79.  
  80. public void calculateLength() {
  81. int begin = Integer.parseInt(getBegin());
  82. int end = Integer.parseInt(getEnd());
  83. int length = end - begin;
  84. setLength(String.valueOf(length));
  85. }
  86. }

  整理完毕。

  

Duplicate Observed Data的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data

    当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...

  2. ORACLE 11g 用Duplicate恢复Data Guard 备库详细过程

    1.先查找备库控制文件路径 先在备库上找出控制文件的路径,通过和主库一样,不过为了以防万一,还是check为好. SQL>  select name from v$controlfile; NA ...

  3. [转] Agile Software Development 敏捷软件开发

    原文作者:kkun 原文地址:http://www.cnblogs.com/kkun/archive/2011/07/06/agile_software_development.html 敏捷是什么 ...

  4. 代码的坏味道(2)——过大的类(Large Class)

    坏味道--过大的类(Large Class) 特征 一个类含有过多字段.函数.代码行. 问题原因 类通常一开始很小,但是随着程序的增长而逐渐膨胀. 类似于过长函数,程序员通常觉得在一个现存类中添加新特 ...

  5. C#重构之道

    定义 重构的定义:在不改变软件可观察行为的前提下,改善其内部结构. 其中,不改变软件行为,是重构最基本的要求.要想真正发挥威力,就必须做到“不需了解软件行为”. 如果一段代码能让你容易了解其行为,说明 ...

  6. Java中有四种常见的Map实现方法

    在 HTML5 之前我们做图片预览主流做法有两种,第一种是通过 Flash 插件来做预览,第二种是 Ajax 实现的假预览,也就是说选择图片文件后,图片其实已经异步上传到服务器,服务器处理后返回图片路 ...

  7. 敏捷软件开发 Agile software Development(转)

    原文链接: http://www.cnblogs.com/kkun/archive/2011/07/06/2099253.html 敏捷软件开发 Agile software Development ...

  8. 《重构——改善既有代码的设计》【PDF】下载

    <重构--改善既有代码的设计>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196358 编辑推荐 重构,一言以蔽之,就是在不改变外 ...

  9. BookNote: Refactoring - Improving the Design of Existing Code

    BookNote: Refactoring - Improving the Design of Existing Code From "Refactoring - Improving the ...

随机推荐

  1. 【Luogu】P1040加分二叉树(区间DP)

    题目链接 区间DP,因为中序遍历的性质:区间[l,r]的任何一个数都可以是该区间的根节点. 更新权值的时候记录区间的根节点,最后DFS输出. 见代码. #include<cstdio> # ...

  2. 【DFS序+树状数组】BNUOJ 52733 Random Numbers

    http://acm.bnu.edu.cn/v3/problem_show.php?pid=52733 [题意] 给定一棵树,这棵树每个点都有一个点权,标号从0开始,0是根结点 修改操作: SEED ...

  3. ShareSDK中微信分享错误总结

    项目中用到微信分享,可向好友或朋友圈分享链接时,分享人可以打开网站,查看消息者却始终不能打开网站.试了N种方法,重写了N次分享模块,均没办法解决. 在无意中查看分享链接时发现,朋友圈里分享后,原始链接 ...

  4. 第一行代码 Android 思维导图

    第一行代码 Android  思维导图

  5. [Inside HotSpot] Serial垃圾回收器 (二) Minor GC

    Serial垃圾回收器Minor GC 1. DefNewGeneration垃圾回收 新生代使用复制算法做垃圾回收,比老年代的标记-压缩简单很多,所有回收代码都位于DefNewGeneration: ...

  6. 读取编码器信息Python2.7和Python3.3版本差异及解决,一次订阅多次调用callback的解决

    1. Python3.3以字节类型返回编码器信息,b'...',BUF: b'\xc3KOO\x00OO\x00OO\x00OO\x00OO\x00\x03\x00\x00\x00\x00\x99R\ ...

  7. windows安装RabbitMQ注意事项

    1.首先下载好ERLANG.RabbitMQ安装包,先安装erlang,设置好环境变量,然后再去安装MQ; 2.别人有两个报错: 一:RabbitMQ安装目录中不允许有空格; 二:安装rabbitmq ...

  8. 框架-数据库定义MD5加密

    1.--定义Md5加密declare @pt_pwd varchar(50)set @pt_pwd = ''set @pt_pwd = substring(sys.fn_sqlvarbasetostr ...

  9. java消息队列怎么用

    消息队列的使用场景是怎样的? 经常听到诸如rebbitmq,activemq,请教一下各位前辈消息队列的使用场景是怎样的,什么时候会用到它   校验用户名等信息,如果没问题会在数据库中添加一个用户记录 ...

  10. Tomcat服务器解析“GET /JavaWebDemo1/1.jsp HTTP/1.1”

    (2)服务器收到http请求报文,返回http响应报文 Tomcat服务器解析“GET /JavaWebDemo1/1.jsp HTTP/1.1” Tomcat服务器解析“GET /JavaWebDe ...