本書的目的是解決如何構(gòu)建一個中大型應用应狱,并且滿足:
- 可測性
- 可重構(gòu)
- 易處理
- 易維護
而對小的應用,不適合本書的原則捻脖,本書在組織上按照:
- 先介紹平時寫PHP代碼遇到的共性問題苛坚,然后給出為什么good, solid,clean code是對于應用的健壯和可維護非常重要
- 接著介紹了一些原則和設計模式
- 最后使用這些原則,介紹了Clean Architecture
先介紹第一部分:The Problem With Code
Writing Good Code is Hard
If it were easy, everyone would be doing it
框架是非常好的胯究,可以幫助我們快速的開發(fā)稍计,但是前期的學習成本往往很高,特別是如果想要深入理解框架裕循,需要花費大量的經(jīng)歷臣嚣。
框架的選擇上也非常有講究净刮,每年都有新的框架產(chǎn)生、消亡茧球,我們要選擇那些文檔好的庭瑰,活力好的框架,并且框架不應該限制的應用太死抢埋,這樣我們的應用能快速的從一個框架切換到另一個框架弹灭。
另一個重要的議題是:庫函數(shù)的使用。
composer和packagist的出現(xiàn)揪垄,讓我們能更方便的使用各種各樣的庫和函數(shù)穷吮,但是使用庫同樣也會和框架一個問題,當庫做升級和廢棄的時候饥努,我們需要花費精力去遷移捡鱼、升級庫函數(shù)。
以上所有的問題都是本書希望能解決的酷愧,本書會通過架構(gòu)來嘗試解決這些問題驾诈。
What is Architecture?
我們寫任何程序的時候溶浴,都會按照某種形式組織乍迄,而軟件架構(gòu)就是我們組織程序的方式,當然這種組織是為了更好的達成軟件的目標士败。
What does Architecture Look Like?
我們應用的所有特性定義了軟件架構(gòu)闯两,這些特性可能是:
- 文件的組織方式
- PHP代碼和Html代碼怎么交互
- 面向過程 or 面向?qū)ο?/li>
- 等等....
所以定義架構(gòu)可能非常的冗長,因此我們會針對一些特點給架構(gòu)起個名字谅将,方便彼此交流漾狼,同時運用這些通用的架構(gòu)模式,也能使我們寫出更容易閱讀和理解的代碼饥臂。
舉個具體的例子:你可能只要說我在前端使用MVC模式逊躁,后端使用API web service,別人就能很容易的理解你整個應用的組織方式了隅熙。
Layers of Software
在面向?qū)ο缶幊讨兄疽拢謱蛹軜?gòu)中的層往往是將功能相同的類放到一起,而分層往往是根據(jù)應用的功能進行劃分的猛们。雖然每個應用分層會各不相同,但是一般都會有:數(shù)據(jù)庫交互層狞洋,業(yè)務層弯淘,api交互。
好的分層架構(gòu)中吉懊,彼此間松耦合庐橙,內(nèi)部高內(nèi)聚假勿。
Examples of Poor Architecture
看好的之前,先看看壞的态鳖,通過分析壞的能幫我們更好的理解為什么要這么去做转培。
Dirty,In-line PHP
<body>
<?php $results = mysql_query(
'SELECT * FROM customers ORDER BY name'
); ?>
<h2>Customers</h2>
<ul>
<?php while ($customer = mysql_fetch_assoc($results)): ?> <li><?= $customer['name'] ?></li>
<?php endwhile; ?>
</ul>
</body>
問題:
-
mysql_*函數(shù)已經(jīng)廢棄浆竭,使得我們升級PHP變得困難
Choosing to use these functions today is choosing heartache tomorrow
-
一層就解決了所有的事
將應用所有的事情都在一層中解決了浸须!應用主要有兩個關(guān)注點:一個是從數(shù)據(jù)庫中獲取數(shù)據(jù),另一個是是對數(shù)據(jù)進行展示邦泄。
-
重構(gòu)的噩夢
考慮下面的變更
- 表名(customers)或者列名(name)變了怎么辦删窒?有多少文件你需要去修改?如果我們要從mysql_換到PDO怎么辦顺囊?如果數(shù)據(jù)不再是從數(shù)據(jù)庫中肌索,而是從Restful API?
- 如果我們開始使用模塊語言特碳,如Twig或者Blade诚亚?我們的數(shù)據(jù)庫邏輯深嵌入Html代碼中,我們必須要重寫所有代碼
- 如果我們想改變名字的顯示方式午乓,我們需要更改多少地方站宗?
代碼不可測
Poor Man's MVC
看完用PHP裸寫應用后,進一步是使用mvc模式硅瞧,下面是一個例子:
class CustomersController { public function indexAction() {
$db = Db::getInstance();
$customers = $db->fetchAll(
'SELECT * FROM customers ORDER BY name'
);
return [
'customers' => $customers
]; }
}
<h2>Customers</h2>
<ul>
<?php foreach ($this->customers as $customer): ?> <li><?= $customer['name'] ?></li>
<?php endforeach; ?>
</ul>
我們讓顯示邏輯和控制邏輯分離了份乒,但是仍然有問題:
- 仍然是硬編碼Querys
- 和Db類強耦合
- 仍然很難測試
- 分了兩個非常大的層
Poor Usage of Database Abstraction
使用Repository設計模式進行重構(gòu):
class CustomersController {
public function usersAction() {
$repository = new CustomersRepository();
$customers = $repository->getAll();
return [
'customers' => $customers
];
}
}
<h2>Customers</h2>
<ul>
<?php foreach ($this->customers as $customer): ?> <li><?= $customer['name'] ?></li>
<?php endforeach; ?>
</ul>
通過使用CustomersRepository
,usersAction
不需要關(guān)心數(shù)據(jù)從哪來腕唧,怎么獲取數(shù)據(jù)或辖。
但是上面的架構(gòu)仍然會有些問題:
-
和
CustomersRepository
強耦合仍然直接實例化出
CustomersRepository
類,意味著依賴于具體實現(xiàn)枣接,而不是抽象颂暇。 -
依賴問題
由于我們?nèi)匀灰蕾囉诰唧w的類,因此在測試時候但惶,不適合單元測試耳鸯。
So how Should this Code Look?
class CustomersController extends AbstractActionController {
protected $customerRepository;
public function __construct(CustomerRepositoryInterface $repository) {
$this->customerRepository = $repository;
}
public function indexAction() {
return [
'users' => $this->customerRepository->getAll()
];
}
}
上面的代碼解決了幾個問題:
- 通過聲明依賴于接口,我們再也不依賴于具體實現(xiàn)了
- 可測性好膀曾,通過實現(xiàn)不同的
CustomerRepositoryInterface
,我們就能模擬各種case - 沒有地方會影響我們升級新的PHP或者函數(shù)庫
- 數(shù)據(jù)來源的變化再也不會影響我們了
Coupling The Enemy
耦合是我們遇到的問題中最普遍的县爬,我們?yōu)榱烁玫睦斫怦詈希磧蓚€例子:
Spaghetti Coupling
<body>
<?php $users = mysqli_query('SELECT * FROM users'); ?>
<ul>
<?php foreach ($users as $user): ?> <li><?= $user['name'] ?></li> <?php endforeach; ?>
</ul>
</body>
上面的代碼耦合非常嚴重添谊,高耦合意味著一旦離開另一個類或功能财喳,將無法工作。上面的例子:一旦離開database,我們不能正常工作了耳高,一旦離開瀏覽器扎瓶,我們也無法正常顯示用戶信息。
OOP Coupling
class UsersController {
public function indexAction() {
$repo = new UserRepository();
$users = $repo->getAll();
return $users;
}
}
UsersController
離開UserRepository
能工作嗎泌枪?不能概荷,因此UsersController
強依賴于UserRepository
。
低耦合誰特別關(guān)心碌燕?
- Developers who refactor their code.
- Developers who like to test their code
- Developers who like to reuse their code
How do we Reduce Coupling?
那怎么減少耦合呢误证?有下面4個方法
- 減少依賴:盡可能將類的職責設計的最少,減少對外部的依賴
- 使用依賴注入(DI)
- 使用接口陆蟆,而不是具體的類
- 使用適配器:不直接依賴于第三方庫雷厂,而是使用適配器的方式,減少對于不可控類的依賴