
在開發大型應用程式時,我們常常會遇到類別之間相互依賴的情況。如果你曾在 CodeIgniter 4 中遇到過 “模型之間載入就會壞掉” 的問題,那麼你很可能陷入了一個相當棘手的循環依賴陷阱。本文將探討循環依賴的問題來源,並提供一些有效的解決方案。
什麼是循環依賴?
循環依賴(Circular Dependency)指的是兩個或多個類別之間互相依賴彼此,這樣的結構會導致初始化的死循環,最終導致應用程式無法正常運行。例如,當模型 A 需要用到模型 B,而模型 B 又需要用到模型 A,這樣的結構會讓程式在不斷地嘗試初始化這些模型時陷入無限迴圈。
舉例來說,假設我們有兩個模型:OrderModel 和 OrderPaymentRecordsModel。
- OrderModel:負責訂單相關的資料處理。
- OrderPaymentRecordsModel:負責付款記錄的資料處理。
在 OrderModel 中,我們需要使用 OrderPaymentRecordsModel 來處理付款記錄,而在 OrderPaymentRecordsModel 中,又需要引用 OrderModel 來取得訂單的資訊。這種互相引用的情況就會導致循環依賴的發生。
問題示例
以下是 OrderModel 和 OrderPaymentRecordsModel 中的代碼示例,這裡展示了它們之間的相互依賴問題:
class OrderModel extends BaseModel
{
protected $order_payment_records;
public function __construct()
{
parent::__construct();
$this->order_payment_records = new OrderPaymentRecordsModel();
}
}
class OrderPaymentRecordsModel extends BaseModel
{
protected $order;
public function __construct()
{
parent::__construct();
$this->order = new OrderModel();
}
}
在這樣的結構中,當你載入 OrderModel 時,它會創建 OrderPaymentRecordsModel 的實例,而這個實例在初始化時又會去創建 OrderModel 的實例,這樣會導致應用程式無法正常運行,甚至直接崩潰。
解決方案
要解決這種循環依賴問題,我們可以採取幾種方法:
1. 延遲載入(Lazy Loading)
延遲載入是一種有效的解決方案,可以避免在建構子中直接創建所有依賴。簡單來說,只有在確實需要使用這些依賴時,我們才進行初始化。
修改後的 OrderPaymentRecordsModel:
class OrderPaymentRecordsModel extends BaseModel
{
protected $order;
protected function getOrderModel()
{
if ($this->order === null) {
$this->order = new OrderModel();
}
return $this->order;
}
public function set_status($id, $status, $response_json = '{}', $remark = '----')
{
$orderModel = $this->getOrderModel();
$order = $orderModel->where('order_id', $order_payment_records['order_id'])->first();
// 其餘代碼...
}
}
這樣做可以確保 OrderModel 只在需要時才會被初始化,避免在建構子中引發循環依賴。
2. 依賴注入(Dependency Injection)
另一種方式是透過依賴注入的方式來解決問題。我們可以在控制器或服務類中將所需的依賴傳入,而不是在模型內部自己創建它們。
例如:
class OrderPaymentRecordsModel extends BaseModel
{
protected $order;
public function __construct(OrderModel $order)
{
parent::__construct();
$this->order = $order;
}
}
在控制器中:
$orderModel = new OrderModel();
$orderPaymentRecordsModel = new OrderPaymentRecordsModel($orderModel);
這樣可以確保所有的依賴都由上層控制器注入,而不是在模型內部自己建立,避免了循環依賴。
3. 重構業務邏輯到服務層
如果兩個模型的交互邏輯非常複雜,將共用的邏輯提取到服務層是一種較好的選擇。這樣可以將模型的責任分開,減少模型之間的直接依賴。
例如,創建一個 OrderService:
class OrderService
{
protected $orderModel;
public function __construct()
{
$this->orderModel = new OrderModel();
}
public function getOrderById($orderId)
{
return $this->orderModel->find($orderId);
}
}
然後在 OrderPaymentRecordsModel 中使用這個服務類:
class OrderPaymentRecordsModel extends BaseModel
{
protected $orderService;
public function __construct()
{
parent::__construct();
$this->orderService = new OrderService();
}
public function set_status($id, $status, $response_json = '{}', $remark = '----')
{
$order = $this->orderService->getOrderById($orderPaymentRecords['order_id']);
// 其餘代碼...
}
}
透過將邏輯移到服務層,我們可以讓模型更加專注於資料處理,減少它們之間的耦合,並提高程式碼的可維護性。
留言