如果想利用单个类做太多事情,其内往往就会出现太多实例变量。一旦如此,Duplicated Code也就接踵而至。
 
 
解决方法:
 
 
1.将类内彼此相关的变量,将它们放在一起。使用Extract Class手法,将彼此相关的变量提炼到新的类。
 
 
2.如果1中的新类适合作为一个子类,那么可以使用Extract Subclass手法。
 
11.对于太多代码的处理办法,分解函数,将大函数分解成若干小函数,这样可以消除重复代码。相关的函数,可以跟着变量,一起被提炼到一个新的类中去,使用Extract Class手法或者Extract Subclass。
 
12.实现11时,要确定客户端如何使用提炼的类,然后,使用Extract Interface手法,为每种使用方式提炼出一个接口。
 
111.如果当前过大的类是GUI类,那么,新提炼的类和当前类,两边要各保留一些重复数据,并保持同步。这要使用Duplicate Observed Data手法。
 
具体处理手法介绍:
 
 
1. Extract Subclass
类中的某些特性只被某些(而非全部)实例用到。
 
出现这种特征,可以创建一个子类,该子类只使用这部分特性。这样,就把子类专有的特性从原来的类中
提炼出来,从而让原来的类变得不那么大。
 
重点是要识别出这种情况的存在,比如:Job Item有一种使用情况,只是使用getUnitPrice,getEmployee,
不使用Job Item的其它特性。那么,就可以把使用getUnitPrice,getEmployee提炼为一个类,在这里使用
Extract Subclass手法。
 
再比如:
1: public class Registration
   2: {
   3:     public NonRegistrationAction Action { get; set; }
   4:     public decimal RegistrationTotal { get; set; }
   5:     public string Notes { get; set; }
   6:     public string Description { get; set; }
   7:     public DateTime RegistrationDate { get; set; }
   8: }
 
 
这个类,有两个使用场景,一个是注册的;一个是非注册的。作为注册实例的时候,它只使用到RegistrationDate,Description,RegistrationTotal方法,没有使用其它方法;
而作为非注册实例来使用的时候,只是使用NonRegistrationAction,Notes方法。
 
符合特征,类中的某些特性只被某些(而非全部)实例用到。这里如果使用Extract Subclass手法, 那么,
处理后,结果是这样:
   1: public class Registration
   2: {
   3:     public decimal RegistrationTotal { get; set; }
   4:     public string Description { get; set; }
   5:     public DateTime RegistrationDate { get; set; }
   6: }
   7:
   8: public class NonRegistration : Registration
   9: {
  10:     public NonRegistrationAction Action { get; set; }
  11:     public string Notes { get; set; }
  12: }

2.Extract Interface
 
“使用一个类”的含义解读,下述情况之一:
1).使用该类的所有责任区。
2).某一组客户只使用类责任区中的一个特定子集。
3).该类需要与所有协助处理某些特定请求的类协作。
 
对于2),3)的情况,将一个类中的这部分责任分离出来,这样有意义。因为,
这样可以更容易看清楚类的责任划分。
 
所以,Extract Interface手法做的事情,就是把原来类中的部分子集,部分责任分离出来。
 
例子:
Employee类,提供了很多功能,但是,有部分类,只使用它的getRate()和hasSpecialSkill()功能。
那么,为了让Employee类变小,可以把它的这两个功能,getRate()和hasSpecialSkill()提炼出来。提炼为接口
interface Billable{
 
  public int getRate();
  public boolean hasSpecialSkill();
}
 
然后Employee类实现接口Billable。
 
这样,那些只需要使用到Employee类getRate(),hasSpecialSkill()功能的类,只需要使用接口Billable就可以,这样,只是暴露了Billable功能给使用者。也就是,只给使用者提供它们可以使用的功能,其它功能子集无需提供。
 
多个使用者,只关注getRate(),hasSpecialSkill()功能,不同的使用者,会要求不同的实现。这时,就可以进行不同的实现来满足使用者的要求。
3.Duplicate Observed Data(复制“被监视的数据”)
场景:
一个GUI类,包含了业务处理代码和用户界面代码。当业务逻辑的增加, 会让这个类越来越大,而且逻辑会复杂。这是因为,一个GUI类同时承担了两种责任,业务处理功能和界面渲染功能。这时候,就需要把业务处理代码从GUI类中分离出来。
 
业务处理代码和界面显示代码分离开之后,方便维护和方便开发。在进行业务处理时,需要使用到界面数据,而界面进行更新的时候,也会需要使用到业务处理的结果。这时,就需要数据在业务层和界面层之间进行同步。
 
而这个手法,可以实现上述同能,业务层(领域层)和界面层的数据同步。
 
 
例子:
下面的内容都是IntervalWindow类:
 
  public class IntervalWindow extends Frame...
java.awt.TextField _startField;
java.awt.TextField _endField;
java.awt.TextField _lengthField; class SymFocus extends java.awt.event.FocusAdapter
{
public void focusLost(java.awt.event.FocusEvent event)
{
Object object = event.getSource();
if (object == _startField)
StartField_FocusLost(event);
else if (object == _endField)
EndField_FocusLost(event);
else if (object == _lengthField)
LengthField_FocusLost(event);
}
}

一个GUI界面,有三个文本框,当某个文本框输入内容,离开时,也就是失去焦点的时候,会触发一个计算。
Start文本框失去焦点的时候,执行StartField_FocustLost方法;
End文本框失去焦点的时候,执行EndField_FocusLost方法;
Length文本框失去焦点的时候,执行LengthField_FocusLost方法。
 
Start文本框和End文本框,失去焦点时,做的事情是,检查文本框的输入是否是整数,如果不是,则将文本框的内容设置为0;然后,计算Length文本框的内容。
 
Length文本框失去焦点的时候,做的事情是,检查文本框的输入是否是整数,如果不是,则将文本框的内容设置为0;然后,计算当前文本框的值。
这是界面操作代码。
 
    void StartField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_startField.getText()))
_startField.setText("0");
calculateLength();
} void EndField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_endField.getText()))
_endField.setText("0");
calculateLength();
} void LengthField_FocusLost(java.awt.event.FocusEvent event) {
if (isNotInteger(_lengthField.getText()))
_lengthField.setText("0");
calculateEnd();
}
void calculateLength(){
try {
int start = Integer.parseInt(_startField.getText());
int end = Integer.parseInt(_endField.getText());
int length = end - start;
_lengthField.setText(String.valueOf(length));
} catch (NumberFormatException e) {
throw new RuntimeException ("Unexpected Number Format Error");
}
}
void calculateEnd() {
try {
int start = Integer.parseInt(_startField.getText());
int length = Integer.parseInt(_lengthField.getText());
int end = start + length;
_endField.setText(String.valueOf(end));
} catch (NumberFormatException e) {
throw new RuntimeException ("Unexpected Number Format Error");
}
}
calculateLength()方法和calculateEnd()方法,它包含了计算的逻辑,还包含了对界面组件的引用。我们现在想做的事情,就是,让这两个方法与界面组件(_startField,_endField,_lengthField)解耦。
 
这里就可以使用到Duplicate Observed Data手法。创建一个领域类,领域类和界面类的数据同步;领域类的数据发生更新后,会将结果传递给界面类。
 
-------------------------------------------------------------------------------------------------------
应用Duplicate Observed Data手法之后,得到如下代码:
 
整个流程就变成这样:
1.GUI类,IntervalWindow初始化时,使用的是Interval这个类的值。
2.当GUI类发生事件变化的时候,把组件的值传递给_subject实例,并进行计算。
3._subject计算完毕,再通知GUI类界面进行更新。
 
在这里,计算的逻辑,被分割到_subject实例(领域类)。
数据的传递流程:
 
领域类,界面初始值数据------>{界面生成(GUI类)------>领域类------>界面}------>{界面生成(GUI类)--->领域类--->界面}
 
 
 
 
public class IntervalWindow extends Frame implements Observer{
 
 private Interval _subject;
 
  public IntervalWindow(){
 
    _subject = new Interval();
    _subject.addObserver(this);
    update(_subject, null);
    
     }
 
  String getEnd() {
        return _subject.getEnd();
    }
 
    void setEnd (String arg) {
        _subject.setEnd(arg);
    }
 
 
 
 
 
 
   void StartField_FocusLost(java.awt.event.FocusEvent event) {
        if (isNotInteger(_startField.getText()))
            _startField.setText("0");
          
        _subject.setStart(_startField.getText());
        _subject.calculateLength();
    }
 
    void EndField_FocusLost(java.awt.event.FocusEvent event) {
        if (isNotInteger(_endField.getText()))
            _endField.setText("0");
 
        _subject.setEnd (_endField.getText());
        _subject.calculateLength();
    }
 
    void LengthField_FocusLost(java.awt.event.FocusEvent event) {
        if (isNotInteger(_lengthField.getText()))
            _lengthField.setText("0");
 
       _subject.setLength(_lengthField.getText());
        _subject.calculateEnd();
    }
 
 
 
   @Override
   public void update(Observable observed, Object arg) {
    
       _endField.setText(_subject.getEnd());
       _startField.setText(_subject.getStart());
       _lengthField.setText(_subject.getLength());
   }
}
 
 
-------------------------------
 
 class Interval extends Observable {
 
    private String _end = "0";
    private String _start = "0";
    private String _length ="0";
 
 
    String getEnd() {
        return _end;
    }
    void setEnd (String arg) {
        _end = arg;
        setChanged();
        notifyObservers();
    }
 
 
    String getStart(){
   
       return _start;
    }
   void setStart(String arg){
        
         _start = arg;
         setChanged();
         notifyObservers();
    }
 
 
   String getLength(){
   
       return _length;
    }
   void setLength(String arg){
        
         _length = arg;
         setChanged();
         notifyObservers();
    }
 
      void calculateLength(){
      try {
        int start = Integer.parseInt(getStart());
        int end = Integer.parseInt(getEnd());
        int length = end - start;
        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;
        setEnd(String.valueOf(end));
      } catch (NumberFormatException e) {
        throw new RuntimeException ("Unexpected Number Format Error");
      }
  }
 

}
 
参考资料:
https://sourcemaking.com/refactoring/duplicate-observed-data
https://lostechies.com/seanchambers/2009/08/20/refactoring-day-20-extract-subclass/
http://www.refactoring.com/catalog/duplicateObservedData.html
http://www.refactoring.com/catalog/extractSubclass.html

Large Class--过大的类--要重构的信号的更多相关文章

  1. 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  2. 重构 之 总结代码的坏味道 Bad Smell (一) 重复代码 过长函数 过大的类 过长参数列 发散式变化 霰弹式修改

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

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

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

  4. Refactoring之——代码的坏味道(二)过大的类 &(三)基本类型偏执

    1.1.2 Large Class(过大的类) 特征:一个类包含过多的字段.方法.代码行. 问题原因: 类通常一开始很小,但是随着程序的增长而逐渐膨胀. 类似于过长方法,程序员通常觉得在一个现存类中添 ...

  5. 面向对象设计:共性VS个性-------继承的粒度和聚合的粒度以及类的重构

    共性和个性 依据面向对象的原理.类是对象的抽象.也就是说.类是一系列的既有共性又有个性的对象的高度概括,类的属性和方法代表了隶属于该类的全部对象的共性,类的每一个对象实例都能够有不同的属性值,这反映了 ...

  6. C# 基于大整数类的RSA算法实现(公钥加密私钥解密,私钥加密公钥解密)

    但是C#自带的RSA算法类RSACryptoServiceProvider只支持公钥加密私钥解密,即数字证书的使用. 所以参考了一些网上的资料写了一个RSA的算法实现.算法实现是基于网上提供的一个大整 ...

  7. Java入门到精通——框架篇之Spring源码分析Spring两大核心类

    一.Spring核心类概述. Spring里面有两个最核心的类这是Spring实现最重要的部分. 1.DefaultListableBeanFactory 这个类位于Beans项目下的org.spri ...

  8. N!的阶乘附带简单大整数类的输入输出(暂时没有深入的了解)

    Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N! 我的思路:就想着大整数类去了,才发现自己还不能很好的掌握,其实这是一个大 ...

  9. C++高精度计算(大整数类)

    Java和Pathon可以不用往下看了 C++的基本数据类型中,范围最大的数据类型不同编译器不同,但是最大的整数范围只有[-2^63-2^63-1](对应8个字节所对应的二进制数大小).但是对于某些需 ...

随机推荐

  1. DAY4敏捷冲刺

    站立式会议 工作安排 (1)服务器配置 已完成对微信小程序登录凭证储存至云端数据库,计划使用微信接口返回的session_id进行转化返回本地,以保持登录态. (2)数据库配置 单词学习记录+用户信息 ...

  2. 《剑指offer》---两个栈实现队列

    本文算法使用python3实现 1.题目描述:   用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型.   时间限制:1s:空间限制:32768K 2.思路描述:   ...

  3. 图解linux安装tomcat(附常用命令)

    本例使用的是centos6.5版本,具体内容如下 一.首先到官方下载tomcat服务 http://tomcat.apache.org/download-70.cgi 二.将tomcat上传至linu ...

  4. 抽象类 C#

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. vue 开发多页应用

    vue 开发多页应用 https://www.cnblogs.com/fengyuqing/p/vue_cli_webpack.html https://segmentfault.com/a/1190 ...

  6. Delphi 制作自定义数据感知控件并装入包(dpk文件)中(与DBText类似的数据感知控件)

    一.基础知识简介: 1.包的命名介绍: 包的命名没有规则,但是建议遵守包的命名约定:包的命名与包的版本相关,包的名称前面几个字符通常表示作者或公司名,也可以是控件的一个描述词,后面紧跟的Std表示运行 ...

  7. 【题解】Uoj79一般图最大匹配

    带花树裸题,感觉带花树强强……不会的勿看此文,解释的可能不对,只是给自己看的!!!如题,带花树即为求一般图最大匹配算法(匈牙利与dinic为二分图最大匹配).推荐论文:2015年<浅谈图的匹配算 ...

  8. 【刷题】BZOJ 1002 [FJOI2007]轮状病毒

    Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下 ...

  9. 【刷题】UOJ #34 多项式乘法

    这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入格式 第一行两个整数 \(n\) 和 \(m\) ,分别表示两个多项式的次数. 第二行 \(n+1\) 个整数,表示第一个多项式的 \( ...

  10. bzoj 1862: [Zjoi2006]GameZ游戏排名系统 & bzoj 1056: [HAOI2008]排名系统

    傻叉了一晚上,把t打成x,然后这题神奇在于输出一段数,不足的不用输出,一开始我的是直接找没有后面就退,然后这样会格式错误囧……然后最后zj的还卡了下空间,于是不用string就过了……string毁一 ...