關注分離

   這兩天在工作中經常會提到spearation of concern這個概念。我想在這篇博客裏講一下我對這個概念的理解。實際上關注分離並不是一個專屬於IT領域的概念。在各個領域我們都在不自覺的考慮如何將任務切割,分配。記得在我讀書的時候,看一些開源代碼,就是不理解爲什麼這些所謂的牛逼代碼寫得那麼繞:明明一個簡單的new就能創建一個實例,偏要用一個工廠類來生成;明明一個new就能解決的問題,偏偏要拆成allocator和constuctor兩個步驟;明明只爲實現一個簡單的邏輯,偏要在前面,後面包很多層。當時不理解爲什麼要這樣,只是覺得這樣很高端大氣上檔次。後來自己代碼寫得多了,書也看了不少,才慢慢體會到這些牛逼代碼的設計初衷,懂得欣賞它們。我們創造出那麼多種模式,發明了那麼多編程理念,實際上核心都圍繞着一點:關注分離。下面我們從最簡單的例子慢慢衍生。

例子1:想要實現一個邏輯,給每一個員工配一輛車。我們最直觀的想法就是需要車,就new一個實例出來唄:

Employee * AllocateCar()

{

        Employee *employee = new Employee();

        ...

        employee->Car = new Car();

        return employee;

}

例子2:我們的需求有一點小小的變化:顯然不同的員工統一的配相同的車是不合適的。經理要有經理的車,HR要有HR的車。

Manager * AllocateCar()

{

        Manager *manager = new Manager();

       ...

        manager->Car = new Car1();

        return manager;

}


HR * AllocateCar()

{

        HR * hr = new HR();

        hr->Car = new Car2();

        ...

       return hr;

}

這樣寫代碼顯然是不好的:但凡有一個新的工種,需要配備一個新車,就得增加一個新的函數。考慮到經理和HR大多數屬性及行爲是一致的,車也是如此,我們很容易想到抽象出一個基類:

EmployeeBase * AllocateCar(EmployeeType type)
{

     EmployeeBase *employee;

     if (type == Type.Manager)

     {

             employee = new Manager();

             ...

             employee->Car = new Car1();

     }

     else if (type == Type.HR)

     {

             employee = new HR();

            ...

            employee->Car = new Car2();

      }

      ...

      return employee;

}
顯然這樣比剛纔好很多的,至少統一了。但是還是不太好:這個函數本應該就負責分配小車,它並不需要關心不同工種配不同的車。它要做得太多了。爲了解決這個,我們可以創建工廠類(如下):

EmployeeBase * AllocateCar(EmployeeType type)
{

      EmployeeBase * employee =  EmployeeFactory.Create(type);

      ...

      employee->Car = EmployeeBase.SelectCar(type);

      return employee;

}

引入工廠類,這個函數不會再變化了:所有變化的東西都轉移到了另外兩個函數中去了。這樣的好處是顯而易見的:關注分離了。 分車的只要管好你分車的邏輯就好了。

其他的事交給其他的模塊。

現在還有問題麼? 實際上這個函數只想做一件事, 給員工分配車。但事實上它做了兩件事:1)配車前創建了員工的實例, 2)配車。顯然通過函數名是無法預料到還有步驟1,這很有可能會導致內存泄漏。鑑於此,我們應該將創建員工上移:

EmployeeBase * AllocateCar(EmployeeBase *employee)

{

      if (employee == NULL) return NULL;

     employee->Car = EmployeeBase.SelectCar(employee->Type);

      return employee;

}

EmployeeBase * CreateEmployee(EmployeeType *type)
{

     EmployeeBase * employee = EmployeeFactory.Create(type);

     ...

     return AllocateCar(employee);

}

這樣關注點就繼續分離了:每一個函數都只負責一個邏輯。

但是這樣還是不夠好。我們想想創建完employee實例後(CreateEmployee) 我們是需要使用它的,用完還需要釋放該實例。於是我們還需要編寫一個ReleaseEmployee函數,並且想着調用它。 這樣並不好。使用它的人往往只關注關於employee的業務邏輯,他不應該管理employee的生命週期。如果employee的生命週期非常長,想管好它也是非常不容易的。於是我們可以引入了依賴注入(Dependency Injection)這個概念。依賴注入容器負責管理依賴關係,生命週期,讓業務邏輯將關注點從全部放在“業務”上。當代碼量龐大的時候這種關注分離的威力就能體現出來了。因爲任何地方出錯,或者需要更新,我們只需要分析,修改,替換一小塊模塊,而不影響其他模塊。從代碼層面上說,這樣的代碼更容易維護,更容易擴展。從公司層面上說,招人更容易:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章