重构-改善既有代码的设计(八):重新组织数据

本文介绍了重构过程中涉及的多种技术,如自封装字段、以对象取代数据值、以对象取代数组等,旨在改善代码的可读性、可维护性和设计。通过这些重构手法,可以提高代码的封装性,减少耦合,以及更好地管理和使用数据对象。
摘要由CSDN通过智能技术生成

1、自封装字段(Self En capsulate Field)

(1)症状:直接访问一个字段,但与字段之间耦合关系逐渐变得笨拙

(2)解决:为这个字段建立取值/设值函数,并且只以这些函数来访问字段

(3)间接访问变量的好处:子类可以通过覆写一个函数而改变取数据的途径

(4)直接访问变量的好处:代码比较容易阅读

  • 为待封装字段建立取值/设值函数
  • 找出该字段的所有引用点,将它们全部改为调用取值/设值函数
  • 将该字段声明为private
  • 复查,确保找出所有引用点

2、以对象取代数据值(Replace Data Value with Object)

(1)症状:有一个数据项需要与其他数据和行为一起使用才有意义

(2)解决:将数据项变为对象

  • 为待替换数值新建一个类,在其中声明一个final字段,其类型和源类中的待替换数值类型一样,然后在新类中加入这个字段的取值函数,再加上一个接受此字段为参数的构造函数
  • 编译
  • 将源类中的待替换数值字段的类型改为前面新建的类
  • 修改源类中该字段的取值函数,令它们调用新类的取值函数
  • 如果源类构造函数中用到了这个待替换字段(多半是赋值动作),我们就修改构造函数,令它们改用新类的构造函数来对字段进行赋值
  • 修改源类中待替换字段的设值函数,令它为新类创建一个实例
  • 编译,测试

class Order...
private String _customer;
public Order(String customer){
    _customer = customer;
}
public String getCustomer(){
    return _customer;
}
public void setCustomer(String arg){
    _customer = arg;
}
private static int numberOfOrdersFor(Collection orders, String customer){
    int result = 0;
    Iterator iter = orders.iterator();
    while(iter.hashNext()){
        Order each = (Order) iter.next();
        if(each.getCustomer().equals(customer)) result++;
    }
    return result;
}
--------------------------------------------------------------------------
重构(以对象取代数据值)之后的代码
class Customer{
    private final String _name;
    public Customer(String name){
        _name = name;
    }
    public String getName(){
        return _name;
    }
}

class Order...
private Customer _customer;
public Order(String customerName){
    _customer = new Customer(customerName);
}
public string getCustomerName(){
    return _customer.getName();
}
public void setCustomer(String customerName){
    _customer = new Customer(customerName);
}

3、将值对象改为引用对象(Change Value to Reference)

(1)症状:从一个类衍生出许多彼此相等的实例,希望将它们替换成同一个对象

(2)解决:将这个值对象变成引用对象

(3)值对象常常用于保存少量不可修改的数据,如果希望加入一些可修改的数据,并确保对任何一个对象的修改都能影响到所有引用此一对象的地方,就需要将这个对象变成一个引用对象

class Customer{
    private final String _name;
    public Customer(String name){
        _name = name;
    }
    public String getName(){
        return _name;
    }
}
//Customer类被下面Order类使用
class Order...
private Customer _customer;
public Order(String customerName){
    _customer = new Customer(customerName);
}
public string getCustomerName(){
    return _customer.getName();
}
public void setCustomer(String customerName){
    _customer = new Customer(customerName);
}

//上述Customer对象还是值对象,每个Order对象还是拥有各自的Customer对象
//希望得到不同的Order对象可以共享同一个Customer对象,意味着:每个客户只对应一个Customer对象
//1、采用工厂方法代替构造函数,在Customer类中定义工厂函数
//2、把原本的构造函数的声明改为private
//3、在Order类中原本调用Customer构造函数的地方改为调用工厂函数
class Customer{
    private final String _name;
    private Customer(String name){
        _name = name;
    }
    public String getName(){
        return _name;
    }
    public static Customer create (String name){
        return new Customer(name);
    }
}
class Order...
private Customer _customer;
public Order(String customerName){
    _customer = Customer.create(customerName);
}
public string getCustomerName(){
    return _customer.getName();
}
public void setCustomer(String customerName){
    _customer = Customer.create(customerName);
}

4、将引用对象改为值对象(Change Reference to Value)

(1)症状:有一个引用对象,很小且不可变,而且不易管理

(2)解决:将它变为一个值对象

(3)在分布式系统和并发系统中,不可变的值对象无需考虑同步问题

(4)不可变概念:以Money类为例,Money类表示钱,其中有“币种”和“金额”两条信息,Money对象是一个不可变的值对象并非意味着薪资不能改变,而是意味着:如果你要改变薪资,就需要使用另一个Money对象来取代现有的Money对象,而不是在现有的Money对象上修改

你和Money对象之间的关系可以改变,但Money对象自身不能改变

  • 检查重构目标是否为不可变对象,或是否可修改为不可变对象
  • 建立equals()和hashCode()
  • 编译与测试
  • 考虑是否可以删除工厂函数,并将构造函数声明为public

 

5、以对象取代数组(Replace Array with Object)

(1)症状:你有一个数组,其中元素各自代表不同的东西

(2)解决:以对象替换数组,对于数组中的每个元素,以一个字段来表示

(3)对象可以运用字段名称和函数名称来传达不同元素的含义

(4)使用对象可以将信息封装起来,并为它加上相关行为

(5)所有对数组的直接访问都转而调用对象中的函数,将对象中保存该数组的字段声明为private

String[] rows = new String[3];
row[0] = "Liverpool";
row[1] = "15";
String name = row[0];
int wins = Integer.parseInt(row[1]);
--------------------------------------------------------
重构(以对象取代数组)后的代码
class Performance...
private String _name;
public String getName(){
    return _name;
}
public void setName(String arg){
    _name = arg;
}
String _wins
public int getWins(){
    return Integer.parseInt(_wins);
}
public void setwins(String arg){
    _wins = arg;
}

6、复制“被监视数据”(Duplicate Observed Data)

(1)症状:有些领域数据置身于GUI控件中,而领域函数需要访问这些数据

(2)解决:将该数据复制到一个领域对象中,建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据

(3)一个良好的系统应该将处理用户界

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值