重構(gòu)筆記-Extra Method

前言

這算是我的讀書筆記吧,最近在看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ā)日記

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市牢贸,隨后出現(xiàn)的幾起案子竹观,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臭增,死亡現(xiàn)場離奇詭異懂酱,居然都是意外死亡,警方通過查閱死者的電腦和手機誊抛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門列牺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拗窃,你說我怎么就攤上這事瞎领。” “怎么了并炮?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵默刚,是天一觀的道長。 經(jīng)常有香客問我逃魄,道長荤西,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任伍俘,我火速辦了婚禮邪锌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘癌瘾。我一直安慰自己觅丰,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布妨退。 她就那樣靜靜地躺著妇萄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咬荷。 梳的紋絲不亂的頭發(fā)上冠句,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音幸乒,去河邊找鬼懦底。 笑死,一個胖子當著我的面吹牛罕扎,可吹牛的內(nèi)容都是我干的聚唐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼腔召,長吁一口氣:“原來是場噩夢啊……” “哼杆查!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起臀蛛,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤根灯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烙肺,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纳猪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了桃笙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氏堤。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖搏明,靈堂內(nèi)的尸體忽然破棺而出鼠锈,到底是詐尸還是另有隱情,我是刑警寧澤星著,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布购笆,位于F島的核電站,受9級特大地震影響虚循,放射性物質(zhì)發(fā)生泄漏同欠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一横缔、第九天 我趴在偏房一處隱蔽的房頂上張望铺遂。 院中可真熱鬧,春花似錦茎刚、人聲如沸襟锐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粮坞。三九已至,卻和暖如春初狰,著一層夾襖步出監(jiān)牢的瞬間捞蚂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工跷究, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敲霍。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓俊马,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肩杈。 傳聞我的和親對象是個殘疾皇子柴我,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容