清潔程式碼:職責 — Janos Pasztor

banq發表於2019-01-09

我聽說你想成為一個更好的程式設計師。您希望使用可重用的部分,並希望更輕鬆地維護舊程式碼。您可能還希望在團隊中更好地工作並確保減少錯誤。
對更好程式碼的渴望通常會讓人們發現“清潔程式碼”這個術語。這很可能是由羅伯特C.“鮑勃叔叔”馬丁創造的 ,他寫了一本同名的書。你可能想給它一個閱讀,但是,我發現它非常冗長。本書介紹了一些基本原則,這些原則可以幫助您編寫模組化程式碼,以便以後可以重用這些模組。在本系列中,我們將介紹他的原則和想法,以及清潔程式碼運動的其他一些作者的原則和想法。
您可能已經注意到,我使用了名稱“module”而不是“class”或“object”。那是因為乾淨的程式碼不是特定於物件導向的程式設計。您可以將乾淨的程式碼原則與您喜歡的任何程式設計範例一起使用,例如,經典的過程程式設計。
那麼讓我們開始討論我們議程上的第一個主題:何時是拆分模組的好時機?

責任
編寫程式碼時,必須以某種方式對其進行分段。組織單位可以是類或模組,具體取決於您的程式設計範例。一般的想法是,一段程式碼應該只處理一個責任。換句話說,做一件事,做得好。
但是責任是什麼?讓我們上一節課,例如,與學校的學生打交道。該課程將保留一個學生的資料。像這樣:

class Student {
  private string name;
  
  public void setName(string name) {
    this.name = name;
  }
  
  public string getName() {
    return this.name;
  }
}

您需要將資料儲存在某個位置,例如,在資料庫中。問題出現了:實現save()儲存此學生記錄的方法是一個好主意嗎?畢竟,這將非常方便:

Student joe = new Student();
joe.setName('Joe');
joe.save();


想象一下以下情況。您在考慮MySQL的情況下實現此類,這是Web世界中相當標準的資料庫引擎。一個忠實的日子,你的老闆來找你並告訴你係統管理員一直抱怨,伺服器超載。在短暫的搜尋之後,您發現您的students桌子非常龐大而且速度很慢,因此您現在決定為您的學生資料實施快取。資料從MySQL讀取並寫入例如Memcache。
所以現在你的Student類需要知道MySQL和Memcache。到目前為止Student,只能讓您輕鬆訪問學生資料的簡單類已經發展到相當大的規模,現在出現了維護問題。有很多程式碼甚至無法測試。但是,嘿,這就是生活吧?

接下來的一週,似曾相識,你的老闆又回到了你的辦公桌前。系統管理員再次抱怨。(他們不能只購買更多的硬體嗎?來吧。)現在這是你的courses桌子造成的問題。您決定使用相同的路徑並將Memcache的程式碼複製到您的Courses班級。

是的,是的,我可以聽到你尖叫,你永遠不會那樣做。您總是決定重構程式碼以避免重複。但相信我,其他人不會。除非您單獨工作,否則您將不得不與那些對重複程式碼具有更高容忍度的人打交道。

那麼我們怎樣才能避免這種情況呢?即使不是您編輯程式碼,我們怎樣才能確保不會發生這種情況?答案在於責任這個詞。用我們的save()方法我們犯了一個錯誤。我們在一個班級中承擔了不止一項責任。該Student班負責儲存學生記錄並將其儲存到資料庫。

用偉大的鮑勃叔叔的話來說,如果一個類只有一個改變的原因,那麼它就有一個責任。

前面的例子很清楚:有兩個職責,儲存/檢索資料和資料結構本身。這些應該解耦,以便我們可以獨立地改變它們。
那麼我們如何解決這個問題呢?記住,我們說過我們想讓一個班級只有一個責任。所以讓我們把它分成兩部分。讓我們按原樣保留原始Student類,並建立第二個類來儲存和檢索Student物件。

class MySQLStudentStorage {
  public Student getById(int id) {
    //...
  }
  
  public void store(Student Student) {
    //...
  }
}

當然,當你幾乎沒有程式碼時,這很容易。但是如果你已經擁有大量依賴save()函式的程式碼,你會怎麼做?那麼,你處境艱難,沒有完美的解決方案。

例如,您可以透過Student類中的呼叫代理:

class Student {
  //...
  
  public void save() {
    storage = new MySQLStudentStorage();
    storage.store(this);
  }
}


使用此代理解決方案將極大地幫助您,因為您可以一次重寫一個模組的程式碼,並且您不需要對生產環境進行大的更改。當您需要拆分類時,幾乎可以在所有情況下使用代理策略。

重構
讓我們看一個不同的例子。想象一下處理財務資料的系統。你知道,這是無聊的東西。您很可能擁有某種型別的資料庫,並且您必須向使用它的人員提供一些報告。這些報告將是Excel表格或某些描述的CSV檔案。
其中一份報告可能是每月收入/支出表。最初,您為財務人員構建了此表,以便他們可以根據您提供的資料執行他們的奇特報告軟體。有一天,令你驚訝的是,執行長走進你的房間,並要求你改變報告的一欄。她希望你很好地格式化數字,這樣她就更容易閱讀。當然,您希望讓您的執行長感到高興,因此您需要改變這一欄。
一切都很順利,直到本月末,財務人員再次嘗試使用該報告並將其匯入到他們的軟體中。一切都崩潰了。他們希望修復報告。我不知道你是否遇到了財務人員,但如果他們說他們希望他們的報告得到修復,那就意味著他們現在想要修復它。畢竟,這是本月底,他們需要提供自己的報告,如果因為你遲到,沒有人會詛咒。
你是做什麼?顯然,你還原了之前的改變。然後你給執行長寫了一封抱歉的信,下次你想要加薪時,你的記分卡肯定不會好看。或者您可以花時間重構程式碼以提供兩個單獨的報告而不是一個報告,並將其作為兩個功能出售給CEO。毋庸置疑,你必須花一些時間在上面,但擁有專門的CEO報告比在你的記錄上留下黑色標記要好得多。此外,下次執行長要求您更改某些內容時,您可以自由地這樣做,因為您確保一個模組只負責一件事,只有一個改變的理由。

結論
如您所見,本文中概述的單一責任原則非常符合商業業務利益。讓您編寫遵循此想法的程式碼符合公司的長遠利益。將變化壓縮到不屬於的類中將導致更大和更大的類,並導致巨大的維護難題。複雜的程式碼也意味著您需要進行的任何更改都需要數月而不是數天,因為您必須解決程式碼中有機發展的混亂。


 

相關文章