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

  整理完毕。

  

posted @ 2017-07-12 16:46  kanite  阅读(418)  评论(0编辑  收藏  举报