在項目的開發過程中,我們經常強調代碼模塊盡量做到 **高內聚低耦合**,那么到底是什么是耦合?怎樣做到低耦合?
很多同學感到疑惑,下面我深入探討這個問題,如何在程序設計中進行解耦合實現低耦合。
>[success]
> ***大家所聽說的依賴注入,控制反轉,AOP(面向切面編程)等等概念其最終的目的都是為了解耦,提高代碼復用和一些其他的功能,熟練掌握這些概念是高手的必經之路,這里對這些概念都做了總結,詳細請參閱文檔中的相關章節***
## 什么是耦合?
>[success]
>**在程序設計中我們通常談到的耦合,是指一個功能模塊和其他模塊之間的依賴關系**

這是一個典型的耦合示例,顯然能看出來類 BB 要正常工作,依賴類 AA,因為在里面實例化了類 AA,這意味著如果我想把類 BB 拿出來,放到其他項目用,將無法工作,那么此時我們就可以說類 AA 和類 BB 耦合度高,或者說有耦合。
如果你想不通為什么不同時把類 AA 也一起拿過去的話,想想如果你在項目自己封裝一個工具類,但是類里面使用了thinkphp框架里自帶的助手函數,那要你把工具類拿到laravel框架里使用,意味著什么?把整個thinkphp框架一起復制過去?參考一下 thinkphp里helper.php里的函數,幾乎都是在函數里調用系統的各個類庫,你想僅僅把一個函數復制過去,毫無意義
一個典型的示例是大家在一些論壇看到的某些帖子稱 比Rbac權限驗證更靈活,更強大的Auth類驗證方式。 參考 http://www.thinkphp.cn/topic/4029.html
**Auth類里的部分代碼**
```
protected function getUserInfo($uid)
{
static $user_info = [];
$user = Db::name($this->config['auth_user']);
// 獲取用戶表主鍵
$_pk = is_string($user->getPk()) ? $user->getPk() : 'uid';
if(!isset($user_info[$uid])) {
$user_info[$uid] = $user->where($_pk , $uid)->find();
}
return $user_info[$uid];
}
```
這個是獲取一個用戶信息的方法,明顯的看到里面用到了 `Db::name()` 方法,意味著如果這個類拿到其他項目,就可能無法使用,因為你無法確定其他項目里是否有這個方法,事實上從tp3.2版本遷移到5.0版本都無法使用,需要改代碼
所以耦合的弊端十分明顯,當然了或許作者在設計的時候就沒有考慮這個問題,這不是錯
但是我們在設計項目的時候,應該需要考慮這個問題,這也是為什么提倡高類聚,低耦合的原因
## 如何降低、解除耦合?
實現低耦合就是對兩類之間進行解耦,解除類之間的直接關系,將直接關系轉換成間接關系,下面來提供幾種解耦思路。
### 1.通過傳入耦合對象的方式
?

這里我們把對象在外面實例化好以后,以參數的形式傳入,這樣就可以實現解耦,因為無需不關心對象是什么對象,只要對象有`test`方法即可
典型的參考示例是thinkphp官方提到過的依賴注入機制,實際上做的就是這個事情,只是他使用的自動傳入參數
http://www.hmoore.net/manual/thinkphp5/215849

?
構造函數里強制指定了要傳入的類型為 `Request `對象
在實例化的時候,就可以自動把這個對象的實例傳進來,關于如何傳進來請閱讀本手冊的`反射類`和`依賴注入`章節
同時這里又有一個問題,那就是在我們的示例代碼里類 BB 里指定了只能調用傳入對象的test方法,如果我想執行更多更負責的邏輯,甚至是調用好幾個類里指定的方法,這里的調用方式就有點力不從心
于是我們可以參考下面一種解耦幅度更大的方式
?
### 2.通過傳入執行邏輯方式

這種方式看起來相對有點隱晦難懂,需要一定的基礎
這里是直接把一個執行邏輯以閉包的形式傳進去執行
里面的方式1比較簡單
方式2涉及 `call_user_func_array` 函數,他接受兩個參數,第一個參數為需要執行的函數,第二個參數為執行第一個參數時候傳進去的參數
方式3為建立函數的反射再調用 invokeArgs 方式執行這個函數,可以參考 `反射類` 章節
他們做的事情是一樣的,利用這個思想,可以在比起之前的解耦方式上更大幅度的擴展
典型的應用可以參考 iThink里extend文件夾下的 `namespace auth ` 中的 Auth 類 其中有個`execClosureList` 方法
這個方法的作用是把多個需要放在事務里執行的數據庫操作邏輯寫到多個閉包里,此方法是 iThink 里十分重要的方法,所有CURD方法都會經過這個方法需要大家十分熟悉 參考 `閉包事物構造器`章節
```
/**
* 閉包事物構造器
*
* @param callable $startTrans 啟動事物注冊方法
* @param callable $commit 提交事物注冊方法
* @param callable $rollback 回滾事物注冊方法
* @param array $list 事物列表
* @param string $err 當事物執行失敗時返回的錯誤信息
* @param null $globalVariable 每個事物都可以作用到的共享變量,會push到每個事物元素的參數上
*
* @return bool
*/
public static function execClosureList(callable $startTrans , callable $commit , callable $rollback , $list = [] , &$err = null , &$globalVariable = null)
{
/* 參數和錯誤信息可不傳
* [
[
function($a, $b) {
//執行成功返回真
} ,
array(
1 ,
2 ,
) ,
'error massage'
] ,
[
function($a, $b) {
//執行成功返回真
} ,
array(
1 ,
2 ,
) ,
] ,
];
* */
call_user_func($startTrans);
try
{
$flag = true;
while (($flag !== false) && ($closure = array_shift($list)))
{
//沒傳參數設置參數為空數組
!is_array($closure[1]) && $closure[1] = [];
//傳全局全局變量吧全局變量push到參數列表
!is_null($globalVariable) && $closure[1][] = &$globalVariable;
//執行閉包
$flag = call_user_func_array($closure[0] , $closure[1]);
($flag === false) && is_string($closure[2]) && ($err = $closure[2]);
}
($flag !== false) ? call_user_func($commit) : call_user_func($rollback);
return $flag;
} catch (\Exception $e)
{
call_user_func($rollback);
$err = $e->getMessage();
return false;
}
}
?
```
- 序言
- 圖片預覽
- 詮釋高效開發
- 提問的智慧
- GIT命令參考
- 安裝composer
- 斷點調試技巧
- 調試環境的搭建
- 調試工具的使用及技巧
- 前置基礎-TP底層講解
- 理解編程的抽象
- 耦合與解耦
- 自動加載
- 反射類
- 控制反轉(IOC)和依賴注入(DI)
- iThink 自定義依賴注入的實現
- 常用設計模式
- SPL標準庫
- 行為-鉤子-插件
- AOP-面向切面
- RBAC和Auth類的本質
- 安裝iThink
- 環境要求
- 代碼下載與環境配置
- 執行安裝
- 體驗測試模塊
- apache配置
- nginx配置
- 系統架構詳解
- 目錄詳解
- 執行流程圖
- 數據字典
- RBAC 權限管理架構
- 系統分層詳解
- 控制器層(controller)
- 邏輯層(logic)
- 視圖層(view)
- 模型層(model)
- 服務層(service)
- 應用包架構詳解
- 目錄結構
- 開發規范
- 數據庫規范
- 編碼規范
- 功能設計原則與規范
- 后臺功能詳解
- 基礎功能
- RBAC + Auth 權限機制
- 應用化功能機制
- 代碼生成器(重要)
- 應用骨架代碼生成
- 數據表 CURD 代碼生成
- 頁面構造器(重要)
- 通用元素構造器
- 表格元素構造器
- 搜索表單元素構造器
- 表單元素構造
- 閉包事物構造器
- 應用的開發
- 函數參考