源碼理解思維的提升
分享一些個(gè)人見解阳欲。
Laravel里面的某些概念童太,就像魔術(shù)一樣,看起來很厲害胸完,當(dāng)知道魔術(shù)怎么變的书释,就會(huì)認(rèn)為也不過如此。所以不必感覺Laravel里有些概念難以理解赊窥。
應(yīng)當(dāng)拋除被框架約束思維的枷鎖爆惧,用PHP設(shè)計(jì)的角度去思考,關(guān)注大概锨能,而不是在在框架層面逐行磨嘰扯再。畢竟源碼那么多芍耘,越是底層變量越多,越抽象熄阻,頻繁的調(diào)用和變量指向斋竞,看到天亮也看不明白。
PHP設(shè)計(jì)的角度去思考怎么理解:
對于Laravel源碼秃殉,代碼很優(yōu)雅坝初,很工程化,很正規(guī)钾军,方便擴(kuò)展鳄袍,這是優(yōu)點(diǎn),壞處就是抽象吏恭,好比垃圾桶里的垃圾都要擺放好拗小,太過于工程化的設(shè)計(jì),會(huì)給寫松散業(yè)務(wù)邏輯的開發(fā)者帶來困惑樱哼。
舉個(gè)例子哀九,Laravel里面的很多契約,好多都是一個(gè)空interface搅幅,放到了vendor下阅束,真的是可要可不要,也確實(shí)有不少人說泰勒·奧特威爾就是炫技盏筐。
所以遲早要被繞暈围俘,必須拋開Laravel里面的各種細(xì)節(jié)砸讳,用不需要太工程化PHP代碼去嘗試實(shí)現(xiàn)它琢融,實(shí)現(xiàn)的過程中會(huì)遇到各種優(yōu)化或者是問題,然后逐步優(yōu)化簿寂。
被框架約束思維的枷鎖怎么理解:
框架發(fā)展了這么久漾抬,源碼的累加,不是讓從頭看的常遂,而是遇到了一類問題纳令,需要追加代碼,是一個(gè)演進(jìn)的過程克胳,通過出現(xiàn)的問題或要優(yōu)化的需求作為驅(qū)動(dòng)來實(shí)現(xiàn)代碼(書寫順序)平绩,而不是通過代碼的作用反推解決的問題(反向反了很難看懂源碼,看不懂干啥的漠另,原因就是這里)捏雌,換句話說,1+1=?是問題笆搓,2是結(jié)果性湿。方向反了只有結(jié)果2纬傲,怎么推理出問題是1+1,還是2+0肤频,既然不知道問題叹括,又怎么知道2的意義是啥,那個(gè)地方要用宵荒?
易經(jīng)思維:像各國人描述烏龜汁雷,得學(xué)很長時(shí)間的各國語言(源碼),隨手畫一個(gè)烏龜(思維)骇扇,全世界的人多能看懂摔竿。
要發(fā)掘的不是源碼這樣寫我怎么看懂,而是這樣寫解決了什么問題少孝。
通俗的演進(jìn)示例
第1步:直接上第一版代碼继低,Test類實(shí)現(xiàn)查詢功能,于是調(diào)用了MySQL類的select方法稍走。
class MySQL {
public function select() {
echo '執(zhí)行查詢操作';
}
}
class Test {
private $driver;
public function __construct() {
$this->driver = new MySQL();
}
public function search() {
return $this->driver->select();
}
}
$crud = new Test();
$crud->search();
第2步:但是如果以后有了Oracle袁翁,PostgreSQL怎么辦?婿脸,代碼內(nèi)部的依賴寫死了粱胜。
所以類的內(nèi)部要寫活,將內(nèi)部的依賴狐树,反轉(zhuǎn)到外邊焙压,控制權(quán)由類的外部控制,通過構(gòu)造方法傳參實(shí)現(xiàn)抑钟,這就是控制反轉(zhuǎn)涯曲,方便代碼解耦。
<?php
class MySQL {
public function select() {
echo '執(zhí)行查詢操作';
}
}
class Test {
private $driver;
public function __construct($driver) {
$this->driver = $driver;
}
public function search() {
return $this->driver->select();
}
}
$crud = new Test(new MySQL);
$crud->search();
第3步:但是隨著項(xiàng)目的復(fù)雜在塔,類越來越多幻件,對象到處都是,得有一個(gè)管事的領(lǐng)導(dǎo)人蛔溃,進(jìn)行統(tǒng)一管理绰沥,于是容器出現(xiàn)了。
容器至少需要兩個(gè)方法贺待,bind和make徽曲,bind用于存儲(chǔ),相當(dāng)于領(lǐng)導(dǎo)人管轄的地域麸塞,分配一塊地區(qū)秃臣,讓這些容器有地方生存。make顧名思義就是制造喘垂,創(chuàng)建出來的這些容器(就是實(shí)例化的過程)讓他們做事甜刻。
bind方法一般不會(huì)直接實(shí)例化對象绍撞,因?yàn)榭赡苡貌簧希谴a流程走到哪里了得院,存是得存起來傻铣,防止后期用得上,這很好理解祥绞,就好比Laravel的Cache模塊非洲,不是每次請求都一定能用得上,但是它仍舊會(huì)被預(yù)先加載蜕径。
class Container {
private $container = [];
public function bind($name, $class) {
return $this->container[$name] = $class;
}
public function make($name) {
$class = $this->container[$name] ?? null;
if(! $class) {
return null;
}
return new $class();
}
}
第4步两踏,整合到第2步的代碼當(dāng)中。
看起來簡單東西搞復(fù)雜了兜喻,確實(shí)梦染。但是框架中,對于大量的類和對象的管理卻很有用朴皆,結(jié)合框架的反射帕识,以及其它機(jī)制,是能夠讓類的管理變的更優(yōu)雅的遂铡,
沒有容器情況下的例子:
要狗肉賬肮疗,張三問李四要債,李四說債務(wù)轉(zhuǎn)移給王五了去問王五要扒接,王五說債務(wù)轉(zhuǎn)移給趙六了你去問他要伪货,彼此踢皮球的環(huán)節(jié)耦合度太高,互相依賴钾怔。
有容器情況下的例子:
相當(dāng)于直接起訴到法院碱呼,一步到位,不管你是誰蒂教。我通過法院的管事人巍举,去解決問題脆荷。
<?php
class MySQL {
public function select() {
echo '執(zhí)行查詢操作';
}
}
class Test {
private $driver;
public function __construct($driver) {
$this->driver = $driver;
}
public function search() {
return $this->driver->select();
}
}
class Container {
private $container = [];
public function bind($name, $class) {
return $this->container[$name] = $class;
}
public function make($name) {
$class = $this->container[$name] ?? null;
if(! $class) {
return null;
}
return new $class();
}
}
$container = new Container();
$container->bind('MySQL', MySQL::class);
$crud = new Test($container->make('MySQL'));
$crud->search();
Laravel容器
laravel容器的源碼在vendor/laravel/framework/src/Illuminate/Container/Container.php
Laravel核心概念凝垛,類比成一個(gè)存放各類對象的數(shù)組,貫穿了整個(gè)Laravel流程蜓谋,用于降低耦合度梦皮,管理并存儲(chǔ)各模塊的類與對象。
一個(gè)框架提供了很多模塊桃焕,就需要很多對象剑肯,這些對象需要統(tǒng)一管理,自動(dòng)實(shí)例化观堂,以及類類型的形參自動(dòng)實(shí)例化的反射機(jī)制让网,于是有了容器呀忧。
IOC(控制反轉(zhuǎn))
類內(nèi)部的依賴,反轉(zhuǎn)到類外邊溃睹,控制權(quán)由類的外部控制而账,通過構(gòu)造方法傳參實(shí)現(xiàn),這就是控制反轉(zhuǎn)因篇,方便代碼解耦泞辐。
上文示例有說明。
DI(依賴注入)
依賴注入是控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式
依賴注入函數(shù)內(nèi)部傳參竞滓,有助于提高代碼的模塊化咐吼、可測試性和靈活性,通俗講就是按(開發(fā)者)需加載商佑。
如下锯茄,UserController依賴于UserService類。在控制器的構(gòu)造函數(shù)中注入了 UserService茶没,而不是在 UserController 中實(shí)例化UserService的實(shí)例撇吞。Laravel的服務(wù)容器的反射機(jī)制會(huì)自動(dòng)解析這個(gè)依賴關(guān)系。
class UserController extends Controller {
protected $userService;
public function __construct(UserService $userService) {
$this->userService = $userService;
}
}
Contract(契約)
看了容器的源碼礁叔,發(fā)現(xiàn)有用到牍颈,順便提一嘴。
聽起來高大上琅关,移除Laravel框架高大上的思維枷鎖煮岁,也就一堆接口醒颖,用于規(guī)范化代碼匠楚,實(shí)現(xiàn)大體布局,子類用它實(shí)現(xiàn)細(xì)節(jié)系任,沒什么神奇的高科技新症。
Provider(服務(wù)提供者)
多個(gè)服務(wù)提供者組成一個(gè)容器步氏,新建的provider需要再config/app.php中聲明。
用于注冊應(yīng)用程序的服務(wù)徒爹、綁定依賴關(guān)系以及其它初始化操作荚醒,真用到的時(shí)候就去實(shí)現(xiàn),沒那么嚴(yán)謹(jǐn)隆嗅。
先調(diào)用register方法界阁,然后會(huì)去調(diào)用boot方法。
這個(gè)在安裝某些插件的時(shí)候回去用這個(gè)胖喳,composer包里的代碼泡躯,不足以支撐當(dāng)前的應(yīng)用場景,所以需要做一些配置,然后就用到了Provider较剃。
app()咕别、resolve()
我看到前公司技術(shù)老大經(jīng)常這樣用,以我目前的認(rèn)知來看写穴,這和直接用命名空間的邏輯去調(diào)用一個(gè)類沒什么區(qū)別顷级。
- app(); 返回服務(wù)容器實(shí)例,參數(shù)可傳遞類名,表示調(diào)用那個(gè)類确垫。
- resolve();app()的別名弓颈, 函數(shù)使用服務(wù)容器解析給定名稱的類或接口的實(shí)例,參數(shù)可傳遞類名删掀,表示調(diào)用那個(gè)類翔冀。
擴(kuò)展閱讀
淺談PHP框架中類成員方法的類類型形參是怎么利用ReflectionClass反射類自動(dòng)實(shí)例化的(應(yīng)該是全網(wǎng)首發(fā))