Extract Class (提炼类)

Summary: 

某个类作了应该由两个类作的事。创建一个新类,将相关字段和函数从旧类搬移到新类。 java

Motivation:

咱们都知道,一个类应该是一个清楚的抽象,处理一些明确的责任,可是在实际工做中,类会不断成长扩展,随着责任不断增长,这个类会变得过度复杂。若是某些数据和某些函数老是一块儿出现,某些数据常常同时变化甚至彼此相依,这就表示应该将它们分离出去。一个有用的测试就是问本身,若是搬移了某些字段和函数,会发生什么事?其余字段和函数是否所以变得无心义? 程序员

另外一个每每在开发后期出现的信号是类的子类化方式。若是你发现子类化只影响类的部分特性,或若是你发现某些特性须要以一种方式来子类化,某些特性则须要以另外一种方式子类化,这就意味着你须要分解原来的类。 并发

Mechanics:

1.决定如何分解类所负的责任。 函数

2.创建一个新类,用以表现从旧类中分离出来的责任。 测试

若是旧类剩下的责任与旧类名不符,为旧类改名 spa

3.创建“从旧类访问新类”的连接关系 code

有可能须要一个双向连接。可是在真正须要它以前,不要创建“重新类通往旧类”的连接 对象

4.对于想搬移的没一个字段,运用Move Field 搬移之。 接口

5.每次搬移后,编译、测试。 事务

6. 使用Move Method将必要函数搬移到新类。先搬移较低层函数(“被其余函数调用”多于“调用其余函数”者),再搬移较高层函数。

7.每次搬移以后,编译、测试

8. 检查,精简每一个类的接口

若是创建起双向链接,检查是否能够将它改成单向链接。

9决定是否公开新类。若是的确须要公开它,就要决定让它成为引用对象仍是不可变的值对象。

范例

咱们从一个简单的person类开始

public class Person
{
    private String name;

    private String officeAreaCode;

    private String officeNumber;

    public String getName()
    {
        return name;
    }

    public String getTelephoneNumber()
    {
        return ( "(" + officeAreaCode + ")" + officeNumber );
    }  public String getOfficeAreaCode()
    {
        return officeAreaCode;
    }

    public void setOfficeAreaCode( String arg )
    {
        officeAreaCode = arg;
    }

    public String getOfficeNumber()
    {
        return officeNumber;
    }

    public void setOfficeNumber( String arg )
    {
        officeNumber = arg;
    }

}
          在这个例子中,咱们能够将与电话号码相关的行为分离到一个独立类中。首先要定义一个TelephoneNumber类来表示“电话号码”这个概念
public class TelephoneNumber
{

}
          而后,创建从Person到TelephoneNumber的连接:
public class Person
{
    private TelephoneNumber officeTelephone = new TelephoneNumber();
...
}

如今,咱们运用Move Field移动一个字段:

public class TelephoneNumber
{
    private String areaCode;

    String getAreaCode()
    {
        return areaCode;
    }

    void setAreaCode( String arg )
    {
        areaCode = arg;
    }
}
public class Person
{ 
    public String getTelephoneNumber()
    {
        return ( "(" + getOfficeAreaCode() + ")" + officeNumber );
    }

    public String getOfficeAreaCode()
    {
        return officeTelephone.getAreaCode();
    }

    public void setOfficeAreaCode( String arg )
    {
        officeTelephone.setAreaCode( arg );
    }

     而后咱们能够移动其余字段,并运用Move Method将相关函数移动到TelephoneNumber类中:

public class TelephoneNumber
{
    private String areaCode;

    private String number;

    public String getTelephnoeNumber()
    {
        return ( "(" + getAreaCode() + ")" + number );
    }

    String getAreaCode()
    {
        return areaCode;
    }

    void setAreaCode( String arg )
    {
        areaCode = arg;
    }

    String getNumber()
    {
        return number;
    }

    void setNumber( String arg )
    {
        number = arg;
    }
}
public class Person
{
    private TelephoneNumber officeTelephone = new TelephoneNumber();

    private String name;

    public String getName()
    {
        return name;
    }

    public String getTelephoneNumber()
    {
        return officeTelephone.getTelephnoeNumber();
    }

    TelephoneNumber getOfficeTelephone()
    {
        return officeTelephone;
    }

}
    下一步要作的决定是:要不要对用户公开这个新类? 咱们能够将Person中与电话号码相关的函数委托至TelephoneNumber,从而彻底隐藏这个新类;也能够直接将它对用户公开。咱们还能够将它公开给部分用户。

若是选择公开新类,就须要考虑别名带来的危险。若是公开了TelephoneNumber,而有个用户修改了对象中的areaCode字段值,咱们又怎么能知道呢?并且,作出修改的可能不是直接用户,而是用户的用户。

面对这个问题,咱们又下列几种选择。

1.容许任何对象修改TelephoneNumber对象的任何部分。这就使得TelephoneNumber对象成为引用对象,因而咱们应该考虑使用Change Value to Reference。这种状况下,Person应该是TelephoneNumber的访问点。

2.不容许任何人不经过Person对象就修改TelephoneNumber对象。为此,咱们能够将TelephoneNumber设为不可修改的,或为它提供一个不可修改的接口。

3. 另外一个办法是:先复制一个TelephoneNumber对象,而后将复制获得的新对象传递给用户。但这可能会形成必定程度的迷惑,由于人们会认为他们能够修改TelephoneNumber对象值。此外,若是同一个TelephoneNumber对象被传递给多个用户,也可能在用户之间形成别名问题。

Extract Class是改善并发程序的一种经常使用技术,由于它使你能够为提炼后的两个类分别加锁。若是你不须要同时锁定两个对象,就没必要这样作。

这里也存在危险性。若是须要确保两个对象被同时锁定,就面临事务问题,须要使用其余类型的共享锁。这是一个复杂的领域,比起通常状况须要更繁重的机制。事务颇有实用性,可是编写事务管理程序则超出了大多数程序员的职责范围。

相关文章
相关标签/搜索