TDD(2) --轉(zhuǎn)自http://www.aygfsteel.com/yandazhi
重構(gòu)以兩種方式和TDD密切相關(guān)。
在以盡可能簡單的手段通過測試之后,(在這個過程中違反了任何編碼規(guī)則)我們就進行重構(gòu)清理。大部分是除去我們?yōu)榱送ㄟ^測試而帶來的重復(fù)。
如果我們是按TDD行事,那么我們就有了適當(dāng)?shù)臏y試的安全網(wǎng),這使得我們有信心進行重構(gòu)。
何時重構(gòu)?
1 重復(fù)的時候
































替代成






2 我們發(fā)現(xiàn)代碼,或者/并且它的意圖不清楚的時候。、
代碼是最重要的交付產(chǎn)品,必須盡可能清楚和可理解。
3 我們嗅到代碼味道,微妙的(或者不那么微妙的)跡象表明代碼有問題。
代碼味道不一定總是表明有問題,但是代碼味道表明我們應(yīng)該仔細察看一下,是否有問題。
如何重構(gòu)?
析取類
看看下面的例子
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');
}
}
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不恰當(dāng)?shù)陌颂郙ovie的特性
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');
}
}
//

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當(dāng)中,應(yīng)該抽象出來。
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');
}
}
{
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()方法暴露出來,其他的細節(jié)都封裝起來。
析取接口
下面是一個有形的類,非常簡單
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);
}
}
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 {
//
}
int size();
void add(Movie movieToAdd);
boolean contains(Movie movieToCheckFor);
}
public class MovieList implements IMovieList {
//

}
析取方法
過于龐大的方法,應(yīng)該進行拆分。下面的方法,每個注釋出現(xiàn)的地方可以析取出來
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);
}
// 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);
}
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;
//..
}
// 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 Engineer extends Employee {
//. . .
}
public class Salesman extends Employee
{
//. . .
}
public class Manager extends Employee
{
//. . .
}
用多態(tài)來替代條件(開關(guān))語句
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";
}
}
}
// 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 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();
}
//
}
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 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);
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);
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);
用工廠方法替代構(gòu)造函數(shù)
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;
}
//
}
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;
}
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();
}
}
}
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 辰 閱讀(239) 評論(0) 編輯 收藏 所屬分類: Test-Driven Development