如果想利用单个类做太多事情,其内往往就会出现太多实例变量。一旦如此,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. 【android】实现手指滑动来切换activity(转)

    http://code.eoe.cn/115 1.jpg外部引用 原始文档 MainActivity.java外部引用 原始文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

  2. js实现倒计时60秒的简单代码

    <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Conten ...

  3. tomcat web页面管理应用配置

    大部分时候,我们的tomcat服务器都不是部署在本机,那么怎么样不通过ftp/sftp方式来将war包部署到tomcat容器呢? tomcat有提供web页面管理应用的功能. 我们来看看怎么配置实现该 ...

  4. [CLR via C#]异常和状态管理

    当CLR检测到某个正在运行的.NET应用程序处于一种特殊的正常执行顺序被打断的状态时,会生成一个异常对象来表示这个错误,并将此对象在方法调用堆栈中向上传送.如果一个程序引发了一个异常却没有处理,CLR ...

  5. shit antd & Merry Christmas bug

    shit antd & Merry Christmas bug https://github.com/ant-design/ant-design/issues/13098 antd 玩大了? ...

  6. Ajax在jQuery中的应用(加载异步数据、请求服务器数据)

    加载异步数据 jQuery中的load()方法 load(url,[data],[callback]) url:被加载的页面地址 [data]:可选项表示发送到服务器的数据,其格式为 key/valu ...

  7. 【SQLAlchemy】SQLAlchemy修改查询字段列名

    SQLAlchemy问题记录 company price quantity Microsoft Google Google Google 要实现脚本 select price, sum(quantit ...

  8. 静态方法不能使用this的原因 当没有实例对象时候 在静态方法里面传入this时会出现空指针异常现象 所以为了防止该现象 静态方法里面不能使用this

    静态方法不能使用this的原因 当没有实例对象时候 在静态方法里面传入this时会出现空指针异常现象 所以为了防止该现象 静态方法里面不能使用this

  9. 可持久化Treap

    终于写了一次可持久化Treap,做的是可持久化序列的模板题. Treap Treap=Tree+Heap,是一个随机化的数据结构.它的每个节点至少有两个关键字,一个是我们要存储的\(val\),一个是 ...

  10. es6常用基础合集

    es6常用基础合集 在实际开发中,ES6已经非常普及了.掌握ES6的知识变成了一种必须.尽管我们在使用时仍然需要经过babel编译. ES6彻底改变了前端的编码风格,可以说对于前端的影响非常巨大.值得 ...