小酌重構系列[12]——去除上帝類


關於上帝類

神說:“要有光”,就有了光。——《聖經》。上帝要是會寫程序,他寫的類一定是“上帝類”。程序員不是上帝,不要妄想成為上帝,但程序員可以寫出“上帝類”。上帝是唯一的,上帝的光芒照耀人間,上帝是很愛面子的,他知道程序員寫了“上帝類”,搶了他的風頭,於是他降下神罰要懲戒程序員。——既然你寫了“上帝類”,那么就將你流放到艱難地修改和痛苦的維護的煉獄中,在地獄之火中永久地熬煉。

你看,上帝也是有脾氣的,你做了什么他都知道,你不能搶他的風頭,否則你就要付出代價,受到相應的懲罰。為息帝怒,咱們還是老老實實地編寫一些“小類”吧。

有些開發者為了貪圖簡便,看到一個現成的類,也不管這個類是做什么的,需要追加功能時,就向這個類里面添加功能代碼。久而久之,使得一些類變成了“上帝類”。什么是“上帝類”?上帝類也叫萬能類,意指做了太多“事情”的類。在開發基於WebForms的應用程序時,Page頁面的后置代碼中包含了訪問數據庫、處理業務邏輯、綁定頁面數據、頁面事件處理等這些事情,這就是上帝類的一個舉證(可能很多人都這么干過)。

上帝類的優缺點

優點

“存在即合理”——上帝類比較適用於一些較小的、穩定的應用開發場景,即那些業務邏輯不復雜、也不需要太多維護的應用程序。
比如:一些小工具的開發,不需要過多地考慮類的粒度和職責划分,樓主博客中用的Windows Live Writer代碼高亮插件就是這么做的。

缺點

上帝類的缺點是顯而易見的,上帝類的顆粒度較大,它缺乏可讀性、可擴展性和可維護性。
上帝類違反了“SRP原則”,上帝類擔任的職責太多了,該做的和不該做的它都做了。
同時也違反了“OCP原則”,上帝類功能之間的耦合性太高了,因此不具備可擴展性,當需求變化時,可能會涉及到大量代碼的修改。

“SRP原則”和“OCP原則”的我就不再贅述了,想了解這兩個原則,請參考該系列的另外兩篇文章:分離職責提取接口

示例

重構前

下面這個CustomerService類,定義了5個方法:

  • CalculateOrderDiscount()方法:結合客戶信息,計算訂單的折扣
  • CustomerIsValid()方法:結合訂單信息,判斷客戶是否有效
  • GatherOrderErrors()方法:結合訂單的商品信息和客戶信息,收集訂單錯誤信息
  • Register()方法:注冊客戶信息
  • ForgotPassword()方法:處理客戶忘記密碼
public class CustomerService
{
    public decimal CalculateOrderDiscount(IEnumerable<Product> products, Customer customer)
    {
        // do work
    }

    public bool CustomerIsValid(Customer customer, Order order)
    {
        // do work
    }

    public IEnumerable<string> GatherOrderErrors(IEnumerable<Product> products, Customer customer)
    {
        // do work
    }

    public void Register(Customer customer)
    {
        // do work
    }

    public void ForgotPassword(Customer customer)
    {
        // do work
    }
}

在業務上,這些方法多少和Customer是有一些關聯的。但這不意味着,只要是和Customer相關的方法都要放到CustomerService中。
這個類還可以在職責上做一些划分,粒度可以控制的在細一些。

重構后

重構后,我們按職責將CustomerService拆分為了CustomerOrderServiceCustomerRegistrationService

public class CustomerOrderService
{
    public decimal CalculateOrderDiscount(IEnumerable<Product> products, Customer customer)
    {
        // do work
    }

    public bool CustomerIsValid(Customer customer, Order order)
    {
        // do work
    }

    public IEnumerable<string> GatherOrderErrors(IEnumerable<Product> products, Customer customer)
    {
        // do work
    }
}

public class CustomerRegistrationService
{

    public void Register(Customer customer)
    {
        // do work
    }

    public void ForgotPassword(Customer customer)
    {
        // do work
    }
}

拆分后,我們可以看到:

  • 這兩個類的語義和它們的命名以及定義在其中的方法都是契合的。
  • 類的粒度變小了,代碼的可讀性增強了,並且有利於將來的擴展、維護、修改。

在開發過程中,我們應該保持一個良好的習慣,為類中追加功能時,盡量確認好類的職責,並控制好類的粒度,這有益於代碼的可讀性、擴展性、維護和修改,這樣就不會被上帝發現了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM