前言
這算是我的讀書筆記吧,最近在看Martin Fowler大神的《重構(gòu) 改善既有代碼的設(shè)計》,決定將書中的代碼換成熟悉的代碼來實現(xiàn)痊项。對于重構(gòu)的概念,我直接引用書中的原話來科普下:
所謂重構(gòu)是這樣一個過程:在不改變代碼外在行為的前提下酥诽,對代碼做出修改鞍泉,以改進程序的內(nèi)部結(jié)構(gòu)。重構(gòu)是一種經(jīng)千錘百煉形成的有條不紊的程序整理方法肮帐,可以最大限度地減少整理過程中引入錯誤的幾率咖驮。本質(zhì)上說,重構(gòu)就是在代碼寫好之后改進它的設(shè)計训枢。
學(xué)習(xí)重構(gòu)的意義何在呢托修?更多的是為了提高自己對于軟件工程的見解,提高自己的技術(shù)恒界。
提煉函數(shù)
Extra Method(提煉函數(shù))是一種常見的重構(gòu)手法睦刃,當你覺得你的代碼需要添加一段注釋才能讓人理解用途的時候,不要猶豫仗处,直接將這些代碼提煉出來眯勾,放在獨立的函數(shù)中。提煉函數(shù)的做法步驟如下:
- 創(chuàng)建一個新函數(shù)婆誓,根據(jù)這個函數(shù)的意圖來對它命名(以“做什么”而不是“怎么做”來命名吃环,比如重寫一個數(shù)組的訪問,使得不發(fā)生越界洋幻。那么我會命名為
func safeObjectAtIndex(index: Int)
而不是func MakeIndexLessThanCountToGetObject(index: Int)
郁轻,這個例子有點蠢) - 將提煉出來的代碼從原函數(shù)中復(fù)制到新建的目標函數(shù)里
- 檢查提煉出的代碼,看看其中是否需要使用到在原函數(shù)中的局部變量或者函數(shù)參數(shù)
- 檢查是否有任何局部變量的值被新提煉的函數(shù)改變,如果有好唯,看看能否將提煉代碼處理為查詢操作竭沫,并返回改變的值。如果很難這么做骑篙,需要使用其它方式進行修改
- 將被提煉的代碼段的局部變量或者函數(shù)參數(shù)傳入給新提煉的函數(shù)
- 處理完所有的局部變量蜕提,進行編譯、測試
1靶端、初始代碼
func printOwing() {
var outstanding: CGFloat = 0.0
// print banner
print("********************************")
print("******* Customer Owes ********")
print("********************************")
// calculate outstanding
for order in _orders {
outstanding += order.amount
}
// print details
print("name: \(name)")
print("amount: \(outstanding)")
}
2谎势、提煉功能單一的代碼塊
這一步驟包括把功能相同單一的代碼提煉出來(比如日志輸出代碼),以及簡單的接收原函數(shù)局部變量作為函數(shù)參數(shù)的提煉函數(shù)(修改局部變量在提煉函數(shù)中的命名使其更符合要求)
func printOwing() {
var outstanding: CGFloat = 0.0
printBanner()
// calculate outstanding
for order in _orders {
outstanding += order.amount
}
printDetails(outstanding)
}
func printBanner() {
print("********************************")
print("******* Customer Owes ********")
print("********************************")
}
func printDetails(amount: CGFloat) {
print("name: \(name)")
print("amount: \(amount)")
}
3杨名、對局部變量進行再賦值
對需要進行計算的局部變量進行提煉脏榆,首先檢查局部變量是否會在原函數(shù)跟提煉代碼中一并發(fā)生改變,如果不會台谍,那么將局部變量作為提煉函數(shù)的返回值使用
func printOwing() {
printBanner()
var outstanding: CGFloat = getOutstanding()
printDetails(outstanding)
}
func printBanner() {
print("********************************")
print("******* Customer Owes ********")
print("********************************")
}
func printDetails(amount: CGFloat) {
print("name: \(name)")
print("amount: \(amount)")
}
func getOutstanding() -> CGFloat {
var result: CGFloat = 0.0
for order in _orders {
result+= order.amount
}
return result
}
ps:在swift中存在元組類型须喂,但是建議不要讓函數(shù)返回多種不同值的元組,這會導(dǎo)致函數(shù)的復(fù)用性降低
實戰(zhàn)例子
今天在項目中自定義的一個tableView表尾視圖需要在數(shù)據(jù)源變化的時候改變自身的高度趁蕊,且高度有一個最低值坞生。已知表尾視圖有個belongView
的對tableView的弱引用,屏幕高度為kScreenHeigth
掷伙,導(dǎo)航欄總體高度為kNavigationBarHeight
(在打開個人熱點時這個值會發(fā)生改變)恨胚,其中最低高度值取決于最下方按鈕addButton
的位置。我的重構(gòu)步驟如下:
1炎咖、初始代碼
func updateViewHeight() {
let tableView: UITableView! = belongView!
var headerHeight = tableView.tableViewHeaderView?.frame.height
headerHeight = (headerHeight == nil ? 0 : headerHeight)
let cellCount = tableView.dataSource.numberOfRowsInSection(tableView: tableView, section: 0)
var viewHeight: CGFloat = kScreenHeight - kNavigationBarHeight - CGFloat(cellCount) * tableView.rowHeight - headerHeight
let buttonBottomDistance: CGFloat = 15.0
let minHeight: CGFloat = CGRectGetMaxY(addButton) + buttonBottomDistance
self.frame.height = max(minHeight, viewHeight)
}
2、將局部變量提煉成函數(shù)獲取
我先把單元格個數(shù)寒波、表頭高度以及最低高度提煉出來
func updateViewHeight() {
let headerHeight: CGFloat = getTableViewHeaderViewHeight()
let cellCount: Int= getTableViewCellsCount()
var viewHeight: CGFloat = kScreenHeight - kNavigationBarHeight - CGFloat(cellCount) * belongView!.rowHeight - headerHeight
let minHeight: CGFloat = getViewMinHeight()
self.frame.height = max(minHeight, viewHeight)
}
func getTableViewHeaderViewHeight() -> CGFloat {
var headerHeight = 0.0
if belongView!.tableViewHeaderView {
headerHeight = (belongView!.tableViewHeaderView?.height)!
}
return headerHeight
}
func getTableViewCellsCount() -> Int {
return (belongView!.dataSource.tableView(tableView: belongView!, numberOfRowsInSection: 0))!
}
func getViewMinHeight() -> CGFloat {
let buttonBottomDistance: CGFloat = 15.0
return CGRectGetMaxY(addButton) + buttonBottomDistance
}
3乘盼、將相同功能的代碼再次提煉
由于代碼中獲取單元格數(shù)量的目的是為了獲取所有單元格高度,因此可以提煉一個獲取單元格總高度的方法getCellsTotalHeight
func updateViewHeight() {
let headerHeight: CGFloat = getTableViewHeaderViewHeight()
var viewHeight: CGFloat = kScreenHeight - kNavigationBarHeight - getCellsTotalHeight() - headerHeight
let minHeight: CGFloat = getViewMinHeight()
self.frame.height = max(minHeight, viewHeight)
}
func getTableViewHeaderViewHeight() -> CGFloat {
var headerHeight = 0.0
if belongView!.tableViewHeaderView {
headerHeight = (belongView!.tableViewHeaderView?.height)!
}
return headerHeight
}
func getCellsTotalHeight() -> CGFloat {
let cellCount: Int= getTableViewCellsCount()
return CGFloat(cellCount) * belongView!.rowHeight
}
func getTableViewCellsCount() -> Int {
return (belongView!.dataSource.tableView(tableView: belongView!, numberOfRowsInSection: 0))!
}
func getViewMinHeight() -> CGFloat {
let buttonBottomDistance: CGFloat = 15.0
return CGRectGetMaxY(addButton) + buttonBottomDistance
}
4俄烁、提煉功能相近的函數(shù)
可以看到updateViewHeight
的目的是為了更新表尾視圖的高度绸栅,那么里面還存在一些計算高度的代碼,將這些代碼做最后的提煉成函數(shù)getAdjustHeight
func updateViewHeight() {
self.frame.height = getAdjustHeight()
}
func getAdjustHeight() -> CGFloat {
let headerHeight: CGFloat = getTableViewHeaderViewHeight()
var viewHeight: CGFloat = kScreenHeight - kNavigationBarHeight - getCellsTotalHeight() - headerHeight
let minHeight: CGFloat = getViewMinHeight()
return max(viewHeight, minHeight)
}
func getTableViewHeaderViewHeight() -> CGFloat {
var headerHeight = 0.0
if belongView!.tableViewHeaderView {
headerHeight = (belongView!.tableViewHeaderView?.height)!
}
return headerHeight
}
func getCellsTotalHeight() -> CGFloat {
let cellCount: Int= getTableViewCellsCount()
return CGFloat(cellCount) * belongView!.rowHeight
}
func getTableViewCellsCount() -> Int {
return (belongView!.dataSource.tableView(tableView: belongView!, numberOfRowsInSection: 0))!
}
func getViewMinHeight() -> CGFloat {
let buttonBottomDistance: CGFloat = 15.0
return CGRectGetMaxY(addButton) + buttonBottomDistance
}
5页屠、將臨時變量替換成提煉的函數(shù)
最后粹胯,我們將必要的局部變量變成函數(shù),并且將計算高度的臨時變量變成函數(shù)調(diào)用
可以看到updateViewHeight
的目的是為了更新表尾視圖的高度辰企,那么里面還存在一些計算高度的代碼风纠,將這些代碼做最后的提煉成函數(shù)getAdjustHeight
func updateViewHeight() {
self.frame.height = getAdjustHeight()
}
func getAdjustHeight() -> CGFloat {
var viewHeight: CGFloat = kScreenHeight - kNavigationBarHeight - getCellsTotalHeight() - getTableViewHeaderViewHeight()
return max(viewHeight, getViewMinHeight())
}
func getTableViewHeaderViewHeight() -> CGFloat {
var headerHeight = 0.0
if belongView!.tableViewHeaderView {
headerHeight = (belongView!.tableViewHeaderView?.height)!
}
return headerHeight
}
func getCellsTotalHeight() -> CGFloat {
return CGFloat(getTableViewCellsCount()) * belongView!.rowHeight
}
func getTableViewCellsCount() -> Int {
return (belongView!.dataSource.tableView(tableView: belongView!, numberOfRowsInSection: 0))!
}
func getViewMinHeight() -> CGFloat {
return CGRectGetMaxY(addButton) + getButtonBottomDistance()
}
func getButtonBottomDistance() -> CGFloat {
return 15.0
}
大功告成
文集:開發(fā)日記