前言:关于缩减参数列的重构手法,Doug Lea对我提出了一个警告:并发编程往往需要使用较长的参数列,因为这样你可以保证传递给函数的参数都是不可被修改的,例如内置型对象和值对象一定是不可变的。通常,你可以使用不可变对象取代这样的长参数列,但另一方面你也必须对此类重构保持谨慎。
多年来,我一直坚守一个很有价值的习惯:明确地将“修改对象状态”的函数(修改函数)和“查询对象状态”的函数(查询函数)分开设计。
动机:函数的名称未能揭示函数的用途。
动机:某个函数需要从调用端得到更多信息。
动机:函数本体不再需要某个参数
动机:某个函数既返回对象状态值,有修改对象状态。
做法:建立两个不同的函数,其中一个负责查询,另一个负责修改。
动机:若干函数做了类似的工作,但在函数本体中却包含了不同的值。
做法:建立单一函数,以参数表达那些不同的值。
代码1:
void tenPercentRaise(){
salary *= 1.1;
}
void fivePercentRaise(){
salary *= 1.05;
}
代码2:上述代码可以替换如下
void raise(double factor){
salary *= (1 + factor);
}
代码1:
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
static Employee create(int type){
switch(type){
case ENGINEER:
return new Engineer();
}
case ENGINEER:
return new Salesman();
}
case ENGINEER:
return new Manager();
}
default :
throw new IllegalArgumentException("Incorrect type code value");
}
代码2:
static Employee createEngineer(){
return new Engineer();
}
static Employee createSalesman(){
return new Salesman();
}
static Employee createManager(){
return new Manager();
}
动机:你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
做法:改为传递整个对象。
代码1:int low = daysTempRange().getLow();
int high = daysTempRange().getHigh();
withinPlan = plan.withinRange(low, high);
代码2:withinPlan = plan.withinRange(daysTempRange());
8、Replace Parameter With Methods(以函数取代参数)
动机:对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。
做法:让参数接受者去除该项参数,并直接调用前一个函数。
代码1:
int basePrice = _quantity * _itemPrice;
discountLevel = getDiscountLevel();
double finalPrice = discountedPrice(basePrice, discountLevel);
代码2:
int basePrice = _quantity * _itemPrice;
double finalPrice = discountedPrice(basePrice);
某些参数总是很自然地同时出现,这时可以以一个对象取代这些参数。价值在于缩短参数列。
做法:新建一个类,用以表现你想替换的一组参数。将这个类设为不可变的。
比如:
private final Date _start;
private final Date _end;
DateRange(Date start, Date end){
_start = start;
_end = end;
}
Date getStart(){
return _start;
}
Date getEnd(){
return _end;
}
动机:类中的某个字段应该在对象创建时被设值,然后就不可再改变。
做法:去掉该字段的所有设值函数。
动机:有一个函数,从来没有被其他任何类用到。应该将这个函数修改为private。
12、Replace Constructor with Factory Method(以工厂函数取代构造函数)
动机:你希望在创建对象时不仅仅是做简单的构建动作。
范式1:根据整数(实际是类型码)创建对象
class Employee{
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
Employee(int type){
_type = type;
}
static create(int type){
return new Employee(type);
}
static Employee create(int type){
switch(type){
case ENGINEER:
return new Engineer();
case SALESMAN:
return new Salesman();
case MANAGER:
return new Manager();
default:
throw new IllegalArgumentException("Incorrect type code value");
}
}
}
调用:Employee eng = Employee.create(Employee.ENGINEER);
范式2:根据字符串创建子类对象
static Employee create(String name){
try{
return (Employee)Class.forName(name).newInstance();
}catch(Exception e){
throw new IllegalArgumentException("Unable to instantiate" + name);
}
}
调用:Employee.create(“Engineer”);
范式3:以明确函数创建子类
class Person...
static Person createMale(){
return new Male();
}
static Person createFemale(){
return new Female();
}
调用:Person kent = Person.createMale();
动机:某个函数返回的对象,需要调用者执行向下转型
做法:将向下转型动作转移到函数中。
Object lastReading(){
return readings.lastElement();
}
Reading lastReading(){
return (Reading)readings.lastElement();
}
动机:某个函数返回一个特定的代码,用以表示某种错误情况。
代码1:int withdraw(int amount){
if(amount > _balance){
return -1;
}
else{
_balance -= amount;
return 0;
}
}
代码2:void withdraw(int amount)throws BalanceException{
if(amount > _balance)throw new BalanceException();
_balance -= amount;
}
代码1:
double getValueForPeriod(int periodNumber){
try{
return _values[periodNumber];
}catch(ArrayIndexOutofBoundsException e){
return 0;
}
}
代码2:
double getValueForPeriod(int periodNumber){
if(periodNumber >= _values.length) return 0;
return _values[periodNumber];
}
代码3:
Resource getResource(){
try{
result = (Resource) _available.pop();
_allocated.push(result);
return result;
}catch(EmptyStackException e){
result = new Resource();
_allocated.push(result);
return result;
}
}
代码4:
Resource getResource(){
Resource result;
if(_available.isEmpty()){
result = new Resource();
_allocated.push(reuslt);
return result;
}
else{
result = (Resource) _available.pop();
_allocated.push(result);
return result;
}
}