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

      整理完毕。

      

  • 相关阅读:
    [置顶] 寻找数组中的值
    详解 Java 的八大基本类型,写得非常好!
    从入门到放弃的 Java 架构师面试题!
    通往大牛之路,百度Java面试题前200页!
    HTML与CSS简单页面效果实例
    JS面向对象
    JS瀑布流效果
    CSS常用操作-图片
    CSS常用操作-导航栏
    CSS常用操作-对齐
  • 原文地址:https://www.cnblogs.com/kanite/p/7156329.html
Copyright © 2020-2023  润新知