`

《重构,改善现有代码的设计》第八章 Duplicate Observed Data

阅读更多
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.FocusAdapter;
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.JFrame;

/**
 * 《重构,改善现有代码的设计》第八章 Duplicate Observed Data
 * 
 * 业务逻辑:
 * 一个JFrame(类IntervalWindow)有三个TextField,分别是start,end和length
 * 如果你修改Start或End,length就会自动成为两者计算所得的长度
 * 如果你修改length,End就会随之改变(start不变)
 * 
 * 主要是使用了Observer模式:
 * start,end和length之间的运算,是与具体界面无关的,应该独立出来(成为一个Subject,观察者模式里面的“目标”)
 * 
 * 关键在于解决以下两个问题:
 * 1.什么时候计算?当界面上的TextField值有变动时,由WindowObserver调用Subject的set方法
 * 2.计算后如何通知界面?Subject调用notifyObservers方法,这个方法会触发WindowObserver的update方法
 * 
 */
public class ObserveGUIWindow {
    public static void main(String[] args) {
        new WindowObserver().init();
    }
}

class WindowObserver  implements Observer {
    
    private TextField startField;
    private TextField endField;
    private TextField lengthField;
    private Subject subject;
    
    public void init() {
        JFrame f = new JFrame("This is a test");
        f.setSize(200, 200);
        Container content = f.getContentPane();
        content.setBackground(Color.white);
        content.setLayout(new FlowLayout());
        
        startField = new TextField("0", 10);
        endField = new TextField("0", 10);
        lengthField = new TextField("0", 10);
        SymFocus listener = new SymFocus();
        startField.addFocusListener(listener);
        endField.addFocusListener(listener);
        lengthField.addFocusListener(listener);
        
        Label startLabel = new Label("start:");
        Label endLabel = new Label("end:");
        Label lengthLabel = new Label("length:");
        content.add(startLabel);
        content.add(startField);
        content.add(endLabel);
        content.add(endField);
        content.add(lengthLabel);
        content.add(lengthField);
        
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        
        subject = new Subject();
        subject.addObserver(this);
        
    }
    
    //更新界面,最新的值(计算后)来自Subject
    public void update(Observable o, Object arg) {
        Subject subject = (Subject)o;
        startField.setText(subject.getStart());
        endField.setText(subject.getEnd());
        lengthField.setText(subject.getLength());
    }
    
    class SymFocus extends FocusAdapter {
        public void focusLost(FocusEvent event) {
            Object object = event.getSource();
            if (object == startField)
                StartFieldFocusLost(event);
            else if (object == endField)
                EndFieldFocusLost(event);
            else if (object == lengthField)
                LengthFieldFocusLost(event);
        }
    }
    
    void StartFieldFocusLost(FocusEvent event) {
        
        //从界面取得输入的值
        String start = startField.getText();
        subject.setStart(isInteger(start) ? start : "0");
        
        //交由Subject计算
        subject.calculateLength();
    }
    
    void EndFieldFocusLost(FocusEvent event) {
        String end = endField.getText();
        subject.setEnd(isInteger(end) ? end : "0");
        subject.calculateLength();
    }
    
    void LengthFieldFocusLost(FocusEvent event) {
        String length = lengthField.getText();
        subject.setLength(isInteger(length) ? length : "0");
        subject.calculateEnd();
    }
    
    boolean isInteger(String str) {
        return str != null && str.matches("[0-9]+");
    }
    
}


class Subject extends Observable {
    
    private String start = "0";
    private String end = "0";
    private String length = "0";
    
    void calculateLength() {
        try {
            int start = Integer.parseInt(getStart());
            int end = Integer.parseInt(getEnd());
            int length = end - start;
            this.setLength(String.valueOf(length));
        } catch (NumberFormatException e) {
            throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    
    void calculateEnd() {
        try {
            int start = Integer.parseInt(getStart());
            int length = Integer.parseInt(getLength());
            int end = start + length;
            this.setEnd(String.valueOf(end));
        } catch (NumberFormatException e) {
            throw new RuntimeException("Unexpected Number Format Error");
        }
    }
    
    public String getStart() {
        return start;
    }
    
    public void setStart(String start) {
        this.start = start;

//下面这两个方法在setter里面可以不调用,改到calculateEnd和calculateLength再调用更好
        this.setChanged();
        this.notifyObservers();
    }
    
    public String getEnd() {
        return end;
    }
    
    public void setEnd(String end) {
        this.end = end;
        this.setChanged();
        this.notifyObservers();
    }
    
    public String getLength() {
        return length;
    }
    
    public void setLength(String length) {
        this.length = length;
        this.setChanged();
        this.notifyObservers();
    }
    
}
0
1
分享到:
评论

相关推荐

    重构-改善既有代码的设计

    第8章 重新组织数据 169 8.1 Self Encapsulate Field(自封装字段) 171 8.2 Replace Data Value with Object(以对象取代数据值) 175 8.3 Change Value to Reference(将值对象改为引用对象) 179 8.4 ...

    重构:改善既有代码的设计.[美]Martin Fowler.epub【文字版手机格式】

    第8章 重新组织数据 8.1 Self Encapsulate Field(自封装字段) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将值对象改为引用对象) 8.4 Change Reference to Value(将...

    重构-改善既有代码的设计 中文版

    第8章 重新组织你的数据 8.1 Self Encapsulate Field(自封装值域) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将实值对象改为引用对象) 8.4 Change Reference to ...

    重构 改善既有代码的设计

     Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *...

    重构-改善既有代码的设计+中文版

     Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *...

    重构-改善既有代码的设计_简体中文

    ### 重构——改善既有代码的设计 #### 知识点概览 《重构——改善既有代码的设计》是一本针对软件开发人员提升代码质量的经典著作。本书不仅深入浅出地讲解了重构的基本概念、方法论,还提供了丰富的重构示例,帮助...

    重构——改善既有代码的设计

     Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *...

    重构改善既有代码的设计中文版

    《重构:改善既有代码的设计》是一本关于如何改进现有代码结构、提高代码可维护性的经典著作。本书由Martin Fowler撰写,详细介绍了各种重构技巧和模式,并通过实际示例展示了如何在不改变软件外部行为的情况下优化...

    重构-改善既有代码的设计(中文版)

     Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *...

    重构,改善既有代码的设计

     Duplicate Observed Data 重复被观察数据   *Change Unidirectional Associationto Bidirectional 将单向关联改为双向   Change Bidirectional Association to Unidirectional 将双向关联改为单向   *...

    重构-改善既有代码的设计(中文版)

    ### 重构——改善既有代码的设计 #### 知识点概览 **重构**是指在不改变软件外部行为的前提下,对代码进行结构上的调整,以提高其可读性、可维护性和扩展性。这一过程通常涉及一系列经过验证的技术,称为重构模式...

    重构-改善既有代码的设计简体中文高清文字pdf

    ### 重构:改善既有代码的设计 #### 知识点概览 重构是软件开发中一个重要的环节,旨在改进现有代码的结构而不改变其外部行为。本文档基于《重构—改善既有代码的设计》这本书的部分内容,提供了多种重构技术的概述...

Global site tag (gtag.js) - Google Analytics