随笔 - 3, 文章 - 152, 评论 - 17, 引用 - 0
数据加载中……

TDD(2) --转自http://www.blogjava.net/yandazhi

重构以两种方式和TDD密切相关。

在以尽可能简单的手段通过测试之后,(在这个过程中违反了任何编码规则)我们就进行重构清理。大部分是除去我们为了通过测试而带来的重复。

如果我们是按TDD行事,那么我们就有了适当的测试的安全网,这使得我们有信心进行重构。   

何时重构?

1  重复的时候

public boolean save() throws IOException {
  
if (outputFile == null{
    
return false;
  }


  FileWriter writer 
= new FileWriter(outputFile);
  movies.writeTo(writer);
  writer.close();
  
return true;
}


public boolean saveAs() throws IOException {
  outputFile 
= view.getFile();
  
if (outputFile == null{
    
return false;
  }


  FileWriter writer 
= new FileWriter(outputFile);
  movies.writeTo(writer);
  writer.close();
  
return true;
}


替代成

public boolean saveAs() throws IOException {
  outputFile 
= view.getFile();
  save();
}


2  我们发现代码,或者/并且它的意图不清楚的时候。、

代码是最重要的交付产品,必须尽可能清楚和可理解。

3  我们嗅到代码味道,微妙的(或者不那么微妙的)迹象表明代码有问题。

代码味道不一定总是表明有问题,但是代码味道表明我们应该仔细察看一下,是否有问题。

如何重构?

析取类
看看下面的例子

public void writeTo(Writer destination) throws IOException {
  Iterator movieIterator 
= movies.iterator();
  
while (movieIterator.hasNext()) {
    Movie movieToWrite 
= (Movie)movieIterator.next();;
    destination.write(movieToWrite.getName());
    destination.write(
'|');
    destination.write(movieToWrite.getCategory().toString());
    destination.write(
'|');

    
try {
      destination.write(Integer.toString(movieToWrite.getRating()));
    } 
catch (UnratedException ex) {
      destination.write(
"-1");
    }
    destination.write(
'\n');
  }
}

MovieList不恰当的包含了太多Movie的特性


public class MovieList {
  
//
  public void writeTo(Writer destination) throws IOException {
    Iterator movieIterator 
= movies.iterator();
    
while (movieIterator.hasNext()) {
      Movie movieToWrite 
=(Movie)movieIterator.next();;
      movieToWrite.writeTo(destination);
    }
}

public classMovie {
  
//

  
public void writeTo(Writer destination) {
    destination.write(getName());
    destination.write(
'|');
    destination.write(getCategory(). toString());
    destination.write(
'|');c

    
try {
      destination.write(Integer.toString(getRating()));

    } 
catch (UnratedException ex) {
      destination.write(
"-1");
    }

    destination.write(
'\n');
  }
}


 写电影列表的工作放在MovieList和Movie当中,应该抽象出来。

public class MovieListWriter 
{
  Writer destination 
= null;

  
public MovieListWriter(Writer aWriter) {
    destination 
= aWriter;
  }

  
public void writeMovieList(MovieList aList) throws IOException {
    Iterator movieIterator 
= aList.getMovies().iterator();
    
while (movieIterator.hasNext()) {
      Movie movieToWrite 
= (Movie)movieIterator.next();;
      writeMovie(movieToWrite);
    }

  
private void writeMovie(Movie aMovie) {
    destination.write(aMovie.getName());
    destination.write(
'|');
    destination.write(aMovie.getCategory().toString());
    destination.write(
'|');

    
try {
      destination.write(Integer.toString(aMovie.getRating()));

    } 
catch (UnratedException ex) {
      destination.write(
"-1");
    }
    destination.write(
'\n');
  }
}


写电影列表的代码集中起来。而且只有writeMovieList()方法暴露出来,其他的细节都封装起来。

析取接口

下面是一个有形的类,非常简单
public class MovieList {
  
private Collection movies = new ArrayList();


  
public int size() {
    
return movies.size();
  }

  
public void add(Movie movieToAdd) {
    movies.add(movieToAdd);
  }

  
public boolean contains(Movie movieToCheckFor) {
    
return movies.contains(movieToCheckFor);
  }
}

如果需要模拟他,我们应该析取接口

public interface IMovieList {
  
int size();
  
void add(Movie movieToAdd);
  boolean contains(Movie movieToCheckFor);
}




public class MovieList implements IMovieList {
  
//
}


析取方法

过于庞大的方法,应该进行拆分。下面的方法,每个注释出现的地方可以析取出来
public void init() {
  
// set the layout
  getContentPane().setLayout(new FlowLayout());

  
// create the list
  movieList = new JList(myEditor.getMovies());
  JScrollPane scroller 
= new JScrollPane(movieList);
  getContentPane().add(scroller);

  
// create the field
  movieField = new JTextField(16);
  getContentPane().add(movieField);

  
// create the add button
  addButton = new JButton("Add");
  addButton.addActionListener(
new ActionListener() {
    
public void actionPerformed(ActionEvent e) {
      myEditor.add(movieField.getText());
      movieList.setListData(myEditor.getMovies());
    }
  });
  getContentPane().add(addButton);
}


分解和命名为成一些不证自明的方法。

public void init() {
  setLayout();
  initMovieList();
  initMovieField();
  initAddButton();
}

private void setLayout() {
  getContentPane().setLayout(
new FlowLayout());
}

private void initMovieList() {
  movieList 
= new JList(getMovies());
  JScrollPane scroller 
= new JScrollPane(movieList);
  getContentPane().add(scroller);
}

private void initMovieField() {
  movieField 
= new JTextField(16);
  getContentPane().add(movieField);
}

private void initAddButton() {
  addButton 
= new JButton("Add");
  addButton.addActionListener(
new ActionListener() {
    
public void actionPerformed(ActionEvent e) {
      myEditor.add(movieField.getText());
      movieList.setListData(getMovies());
    }
  });
  getContentPane().add(addButton);
}


用子类代替类型代码

public class Employee {
  
// 0 - engineer, 1 - salesman, 2 - manager
  private int employeeType;

  
//..
  }


替代成

abstract public class Employee {
  
//. . .
}

public class Engineer extends Employee {
  
//. . .
}

public class Salesman extends Employee 
{
  
//. . .
}

public class Manager extends Employee 
{
  
//. . .
}


用多态来替代条件(开关)语句

public class Employee {
  
// 0 - engineer, 1 - salesman, 2 - manager
  private int employeeType;

  
public String departmentName() {
    
switch (employeeType) {
      
case 0:
        
return "Engineering";
      
case 1:
        
return "Sales";
      
case 2:
        
return "Management";
      
default:
        
return "Unknown";
    }
  }
}


替代为
abstract public class Employee {
  
public abstract String departmentName();
}


public class Engineer extends Employee {
  
public String departmentName() {
    
return "Engineering";
  }
}


public class Salesman extends Employee {
  
public String departmentName() {
    
return "Sales";
}
}


public class Manager extends Employee {
  
public String departmentName() {
    
return "Management";
  }
}


模板方法

public class Engineer extends Employee {
  
public String asXML() {
    StringBuffer buf 
= new StringBuffer();
    buf.append(
"<employee name=\"");
    buf.append(getName());
    buf.append(
"\" department=\"Engineering\">");
    //
    return buf.toString();
  }
  
//. . .
}





public class Salesman extends Employee {
  
public String asXML() {
    StringBufer buf 
= new StringBuffer();
    buf.append(
"<employee name=\"");
    buf.append(getName());
    buf.append(
"\" department=\"Sales\">");
    //. . .
    return buf.toString();
  }
  
//
}


public class Manager extends Employee {
  
public String asXML() {
    StringBufer buf 
= new StringBuffer();
    buf.append(
"<employee name=\"");
    buf.append(getName());
    buf.append(
"\" department=\"Management\">");
    //. . .
    return buf.toString();
  }
  
//
}


用employee很好地解决了问题

public class Employee {
  
public String asXML() {
    StringBuffer buf 
= new StringBuffer();
    buf.append(
"<employee name=\"");
    buf.append(getName());
    buf.append(
"\" department=\"");
    buf.append(departmentName());
    buf.append(
"\">");
    //
    return buf.toString();
  }
  
//. . .
}



引入解释变量

public Money calculateTotal() {
  
return getSubtotal().plus((getTaxableSubtotal().times(0.15))).minus((getSubtotal().asDouble()> 100.0)?(getSubtotal().times(0.10)):0);

public Money calculateTotal() {
  Money subtotal 
= getSubtotal();
  Money tax 
= getTaxableSubtotal().times(0.15);
  Money total 
=subtotal.plus(tax);
  boolean qualifiesForDiscount 
= getSubtotal().asDouble() 
> 100.0;
  Money discount 
= qualifiesForDiscount
                         
?subtotal.times(0.10)
                         :newMoney(
0.0);
  
return total.minus(discount);

用工厂方法替代构造函数

public classRating {
  
private int value = 0;
  
private String source = null;
  
private String review = null;

  
public Rating(int aRating) {
    
this(aRating, "Anonymous""");
  }

  
public Rating(int aRating, String aRatingSource) {
    
this(aRating, aRatingSource, "");
  }

  
public Rating(int aRating, String aRatingSource, String aReview) {
    value 
= aRating;
    source 
= aRatingSource;
    review 
= aReview;
  }

  
//
}



public static Rating newAnonymousRating(int value) {
  
return new Rating(value, "Anonymous""");
}

public static Rating newRating(int value, String source) {
  
return new Rating(value, source, "");
}

public static Rating newReview(int value, String source, String review) {
  
return new Rating(value, source, review);
}

private Rating(int aRating, String aRatingSource, String aReview) {
  value 
= aRating;
  source 
= aRatingSource;
  review 
= aReview;
}


用代理代替继承

public class Department extends Vector {
}

public class Department {
  
private Vector employees = new Vector();

  
public void hire(Employee newHire) {
    employees.add(newHire);
  }

  
public DepartmentIterator iterator() {
    
return new DepartmentIterator();
  }

  
public class DepartmentIterator {
    Iterator underlying 
= employees.iterator();

    
public boolean hasNext() {
      
return underlying.hasNext();
    }

    
public Employee next() {
      
return (Employee)underlying.next();
    }
  }
}


posted on 2005-07-25 12:26 阅读(227) 评论(0)  编辑  收藏 所属分类: Test-Driven Development


只有注册用户登录后才能发表评论。


网站导航: