這兩天在工作中經常會提到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)這個概念。依賴注入容器負責管理依賴關係,生命週期,讓業務邏輯將關注點從全部放在“業務”上。當代碼量龐大的時候這種關注分離的威力就能體現出來了。因爲任何地方出錯,或者需要更新,我們只需要分析,修改,替換一小塊模塊,而不影響其他模塊。從代碼層面上說,這樣的代碼更容易維護,更容易擴展。從公司層面上說,招人更容易:)