桃源谷

心灵的旅行

人生就是一场旅行,不在乎旅行的目的地,在乎的是沿途的风景和看风景的心情 !
posts - 32, comments - 42, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

重構, 第一個案例

Posted on 2009-02-02 12:52 lymons 阅读(355) 评论(0)  编辑 收藏 引用 所属分类: C++
From 2008精选
重構, 第一個案例
Refactoring, a First Example

這是一個影片出租店用的程式,計算每位顧客的消費金額並列
印報表(statement)。操作者告訴程式:顧客租了哪些影片、租期多長,程式便
根據租賃時間和影片類型算出費用。影片分為三類:普通片、兒童片和新片。除
了計算費用,還要為常客計算點數;點數會隨著「租片種類是否為新片」而有不
同。
我以數個classes 表現這個例子?的元素。圖1.1 是一張UML class diagram(類別
圖),用以顯示這些classes。

1134571807966_1238.jpg


  1// movie.cpp : Defines the entry point for the console application.
  2//
  3#include <string>
  4#include <vector>
  5#include <iostream>
  6
  7using namespace std;
  8
  9
 10//  Refactoring, a First Example, step1, (~p5)
 11
 12class Movie {
 13public:
 14 enum TYPE {
 15  REGULAR,
 16  NEW_RELEASE,
 17  CHILDRENS
 18 }
;
 19
 20private:
 21 string _title;  //movie name
 22 int _priceCode; //price code
 23
 24public:
 25 //default construtor for `Rental::Rental(Movie, int)'
 26 Movie() {
 27  _title = "unname"
 28  _priceCode = 0;
 29 }

 30 Movie(string title, int priceCode){
 31  _title = title;
 32  _priceCode = priceCode;
 33 }

 34
 35 int getPriceCode() {
 36  return _priceCode;
 37 }

 38
 39 void setPriceCode(int arg) {
 40  _priceCode = arg;
 41 }

 42
 43 string getTitle() {
 44  return _title;
 45 }

 46}
;
 47
 48class Rental {
 49private:
 50 Movie _movie;
 51 int _daysRented;
 52
 53public:
 54 Rental(Movie movie, int daysRented) {
 55  _movie = movie;
 56  _daysRented = daysRented;
 57 }

 58
 59 int getDaysRented() {
 60  return _daysRented;
 61 }

 62
 63 Movie getMovie() {
 64  return _movie;
 65 }

 66}
;
 67
 68typedef vector<Rental> Vector;
 69typedef vector<Rental>::iterator Reniter;
 70
 71class Customer {
 72private:
 73 string _name;
 74 Vector _rentals;
 75
 76public:
 77 Customer(string name) {
 78  _name = name;
 79 }

 80
 81 void addRental(Rental arg) {
 82  _rentals.push_back(arg);
 83 }

 84
 85 string getName() {
 86  return _name;
 87 }

 88
 89 string statement() {
 90  double totalAmount = 0;
 91  int frequentRenterPoints = 0;
 92  Reniter iter;
 93  char amount[32];
 94  string result = string("Rental Record for "+ getName() + string("\n");
 95
 96  for (iter = _rentals.begin(); iter != _rentals.end(); ++ iter) {
 97   double thisAmount = 0;
 98   Rental each = *iter;
 99
100   //determine amounts for each line
101   switch(each.getMovie().getPriceCode()){
102  case Movie::REGULAR:
103   thisAmount += 2;
104   if(each.getDaysRented()>2)
105    thisAmount += (each.getDaysRented()-2)*1.5;
106   break;
107
108  case Movie::NEW_RELEASE:
109   thisAmount += each.getDaysRented()*3;
110   break;
111
112  case Movie::CHILDRENS:
113   thisAmount += 1.5;
114   if(each.getDaysRented()>3)
115    thisAmount += (each.getDaysRented()-3)*1.5;
116   break;
117   }

118
119   // add frequent renter points?
120   frequentRenterPoints ++;
121   // add bonus for a two day new release rental
122   if ((each.getMovie().getPriceCode() == Movie::NEW_RELEASE) &&
123    each.getDaysRented() > 1)
124    frequentRenterPoints ++;
125
126   // show figures for this rental?
127   snprintf(amount, 32,"%f\n",thisAmount);
128   result += string("\t"+ each.getMovie().getTitle() + string("\t"+
129    string(amount);
130   totalAmount += thisAmount;
131  }

132
133  // add footer lines? 
134  snprintf(amount, 32"%f\n",totalAmount);
135  result += string("Amount owed is "+ string(amount);
136  snprintf(amount, 32,"%d",frequentRenterPoints);
137  result += string("You earned "+ string(amount) +
138   string(" frequent renter points");
139  return result;
140 }

141}
;
142
143int main(int argc, char* argv[])
144{
145 cout<<"Refactoring, a First Example, step1"<<endl;
146
147 Movie m1 = Movie("Seven", Movie::NEW_RELEASE);
148 Movie m2 = Movie("Terminator", Movie::REGULAR);
149 Movie m3 = Movie("Star Trek", Movie::CHILDRENS);
150
151 Rental r1 = Rental(m1, 4);
152 Rental r2 = Rental(m1, 2);
153 Rental r3 = Rental(m3, 7);
154 Rental r4 = Rental(m2, 5);
155 Rental r5 = Rental(m3, 3);
156
157 Customer c1 = Customer("jjhou");
158 c1.addRental(r1);
159 c1.addRental(r4);
160
161 Customer c2 = Customer("gigix");
162 c2.addRental(r1);
163 c2.addRental(r3);
164 c2.addRental(r2);
165
166 Customer c3 = Customer("jiangtao");
167 c3.addRental(r3);
168 c3.addRental(r5);
169
170 Customer c4 = Customer("yeka");
171 c4.addRental(r2);
172 c4.addRental(r3);
173 c4.addRental(r5);
174
175 cout <<c1.statement() <<endl;
176 cout <<c2.statement() <<endl;
177 cout <<c3.statement() <<endl;
178 cout <<c4.statement() <<endl;
179 
180 return 0;
181}

182
183

 

1134571894089_6521.jpg
重构后的C++代码

  1// movie-ref.cpp : Defines the entry point for the console application.
  2//
  3#include <string>
  4#include <vector>
  5#include <iostream>
  6
  7using namespace std;
  8//  Refactoring, a First Example, step7, (~p52)
  9
 10enum TYPE
 11{
 12  REGULAR,
 13  NEW_RELEASE,
 14  CHILDRENS
 15}
;
 16
 17class Price
 18{
 19public:
 20  virtual int getPriceCode () = 0;
 21  virtual double getCharge (int daysRented) = 0;
 22
 23  int getFrequentRenterPoints (int daysRented)
 24  {
 25    return 1;
 26  }

 27
 28}
;
 29
 30class ChildrensPrice:public Price
 31{
 32public:
 33  int getPriceCode ()
 34  {
 35    return CHILDRENS;
 36  }

 37
 38  double getCharge (int daysRented)
 39  {
 40    double result = 1.5;
 41    if (daysRented > 3)
 42      result += (daysRented - 3* 1.5;
 43    return result;
 44  }

 45}
;
 46
 47class NewReleasePrice:public Price
 48{
 49public:
 50  int getPriceCode ()
 51  {
 52    return NEW_RELEASE;
 53  }

 54
 55  double getCharge (int daysRented)
 56  {
 57    return daysRented * 3;
 58  }

 59
 60  int getFrequentRenterPoints (int daysRented)
 61  {
 62    return (daysRented > 1? 2 : 1;
 63  }

 64}
;
 65
 66class RegularPrice:public Price
 67{
 68public:
 69  int getPriceCode ()
 70  {
 71    return REGULAR;
 72  }

 73
 74  double getCharge (int daysRented)
 75  {
 76    double result = 2;
 77    if (daysRented > 2)
 78      result += (daysRented - 2* 1.5;
 79    return result;
 80  }

 81}
;
 82
 83class Movie
 84{
 85private:
 86  string _title;  //movie name
 87  Price *_price;  //price code
 88
 89public:  
 90  //default construtor for `Rental::Rental(Movie&, int)'
 91  Movie ()
 92  {
 93    _title = "unname";
 94    setPriceCode (0);
 95  }

 96  
 97  Movie (string title, int priceCode)
 98  {
 99    _title = title;
100    setPriceCode (priceCode);
101  }

102  
103  Movie (const Movie& movie)
104  {
105    _title = movie._title;
106    setPriceCode (movie._price->getPriceCode());
107  }

108  
109  ~Movie()
110  {
111    delete _price;
112  }

113
114  int getPriceCode ()
115  {
116    return _price->getPriceCode ();
117  }

118
119  void setPriceCode (int arg)
120  {
121    switch (arg)
122    {
123      case REGULAR:
124        _price = new RegularPrice ();
125      break;
126      case CHILDRENS:
127        _price = new ChildrensPrice ();
128      break;
129      case NEW_RELEASE:
130        _price = new NewReleasePrice ();
131      break;
132      default:
133        cout << "Incorrect Price Code" << endl;
134      break;
135      }

136  }

137
138  string getTitle ()
139  {
140    return _title;
141  }

142
143  double getCharge (int daysRented)
144  {
145    return _price->getCharge (daysRented);
146  }

147
148  int getFrequentRenterPoints (int daysRented)
149  {
150    return _price->getFrequentRenterPoints (daysRented);
151  }

152}
;
153
154class Rental
155{
156private:
157  Movie _movie;
158  int _daysRented;
159
160public:
161  Rental (Movie& movie, int daysRented)
162  {
163    _movie = movie;
164    _daysRented = daysRented;
165  }

166
167  int getDaysRented ()
168  {
169    return _daysRented;
170  }

171
172  Movie getMovie ()
173  {
174    return _movie;
175  }

176
177  double getCharge ()
178  {
179    return _movie.getCharge (_daysRented);
180  }

181
182  int getFrequentRenterPoints ()
183  {
184    return _movie.getFrequentRenterPoints (_daysRented);
185  }

186}
;
187
188typedef vector < Rental > Vector;
189typedef vector < Rental >::iterator Reniter;
190
191class Customer
192{
193private:
194  string _name;
195  Vector _rentals;
196
197public:
198    Customer (string name)
199  {
200    _name = name;
201  }

202
203  void addRental (Rental& arg)
204  {
205    _rentals.push_back (arg);
206  }

207
208  string getName ()
209  {
210    return _name;
211  }

212
213  string statement ()
214  {
215    Reniter iter;
216    char amount[32];
217    string result =
218      string ("Rental Record for "+ getName () + string ("\n");
219
220    for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
221    {
222      Rental each = *iter;
223      // show figures for this rental
224      snprintf (amount, 32"%f\n", each.getCharge ());
225      result += string ("\t"+ each.getMovie ().getTitle () 
226        + string ("\t"+ string (amount);
227    }

228      // add footer lines
229    snprintf (amount, 32"%f\n", getTotalCharge ());
230    result += string ("Amount owed is "+ string (amount);
231    snprintf (amount, 32"%d", getTotalFrequentRenterPoints ());
232    result += string ("You earned "+ string (amount) +
233      string (" frequent renter points");
234    return result;
235  }

236
237  string htmlStatement ()
238  {
239    Reniter iter;
240    char amount[32];
241    string result =
242      string ("<H1>Rentals for <EM>"+ getName () +
243      string ("</EM></H1><P>\n");
244
245    for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
246    {
247      Rental each = *iter;
248      // show figures for this rental
249      snprintf (amount, 32"%f<BR>\n", each.getCharge ());
250      result += each.getMovie ().getTitle () + string (":"+ string (amount);
251    }

252
253    // add footer lines?    
254    snprintf (amount, 32"%f</EM><P>\n", getTotalCharge ());
255    result += string ("<P>You owe <EM>"+ string (amount);
256    snprintf (amount, 32"%d</EM> frequent renter points<P>",
257       getTotalFrequentRenterPoints ());
258    result += string ("On this rental you earned <EM>"+ string (amount);
259    return result;
260  }

261
262  int getTotalFrequentRenterPoints ()
263  {
264    int result = 0;
265    Reniter iter;
266    for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
267    {
268      Rental each = *iter;
269      result += each.getFrequentRenterPoints ();
270    }

271    return result;
272  }

273
274  double getTotalCharge ()
275  {
276    double result = 0;
277    Reniter iter;
278    for (iter = _rentals.begin (); iter != _rentals.end (); ++iter)
279    {
280      Rental each = *iter;
281      result += each.getCharge ();
282    }

283    return result;
284  }

285
286}
;
287
288int
289main (int argc, char *argv[])
290{
291  cout << "Refactoring, a First Example, step7" << endl;
292
293  Movie m1 = Movie ("Seven", NEW_RELEASE);
294  Movie m2 = Movie ("Terminator", REGULAR);
295  Movie m3 = Movie ("Star Trek", CHILDRENS);
296
297  Rental r1 = Rental (m1, 4);
298  Rental r2 = Rental (m1, 2);
299  Rental r3 = Rental (m3, 7);
300  Rental r4 = Rental (m2, 5);
301  Rental r5 = Rental (m3, 3);
302
303  Customer c1 = Customer ("jjhou");
304  c1.addRental (r1);
305  c1.addRental (r4);
306
307  Customer c2 = Customer ("gigix");
308  c2.addRental (r1);
309  c2.addRental (r3);
310  c2.addRental (r2);
311
312  Customer c3 = Customer ("jiangtao");
313  c3.addRental (r3);
314  c3.addRental (r5);
315
316  Customer c4 = Customer ("yeka");
317  c4.addRental (r2);
318  c4.addRental (r3);
319  c4.addRental (r5);
320
321  cout << c1.statement () << endl;
322  cout << c2.statement () << endl;
323  cout << c3.statement () << endl;
324  cout << c4.statement () << endl;
325
326  return 0;
327}

328
329
1134572212733_8113.jpg

結語
這是一個簡單的例子,但我希望它能讓你對於「重構是什麼樣子」有一點感覺。
例如我已經示範了數個重構準則,包括Extract Method(110)、Move Method(142)、
Replace Conditional with Polymorphism(255)、Self Encapsulate Field(171) 、
Replace Type Code with State/Strategy(227)。所有這些重構行為都使責任的分
配更合理,程式碼的維護更輕鬆。重構後的程式風格,將十分不同於程序式
(procedural)風格,後者也許是某些人習慣的風格。不過一旦你習慣了這種重構
後的風格,就很難再回到(再滿足於)結構化風格了。
這個例子給你的最重要一課是「重構的節奏」:測試、小修改、測試、小修改、
測試、小修改…。正是這種節奏讓重構得以快速而安全地前進。


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


我的个人简历第一页 我的个人简历第二页