Laravel 中的 SOLID 原則:完整範例
SOLID 是一套物件導向軟體開發的原則。
這些原則有助於確保軟體可維護、可重複使用和可擴展。
在本文中,我們將更深入探討 SOLID 原則在 Laravel 的應用
預計閱讀時間: 5 minuti
原則 SOLID 他們是:
- 單一職責原則 (SRP)
- 開閉原理(OCP)
- 里氏替換原理 (LSP)
- 介面隔離原則(ISP)
- 依賴倒置原則(DIP)
如何在 Laravel 實現 SOLID 原則
- 單一職責原則(SRP) 這原則規定,一個類別應該只有一個改變的理由。換句話說,一個類別應該只有一個職責。
- 範例:考慮一個名為
Order
。我們可以將與訂單相關的所有功能放在一個類別中,而不是將其分解為較小的類,例如OrderCalculator
,OrderRepository
,而OrderMailer
.
- 範例:考慮一個名為
- 打開關閉 原則 (OCP) 該原則指出,類別應該對擴展開放,但對修改關閉。換句話說,我們應該能夠在不更改現有程式碼的情況下新增功能。
- 範例:考慮一個名為
PaymentGateway
。我們可以為每個付款方式建立一個新類別來擴展該類,而不是每次添加新的付款方式時都修改此類。PaymentGateway
班級。
- 範例:考慮一個名為
- 替代原則 利斯科夫 (LSP): 此原則規定超類別的物件必須能夠用子類別的物件替換,而不會影響程式的正確性。
- 範例:考慮一個名為
Shape
有子類Circle
,Rectangle
,而Square
。如果我們有一個接受類型物件的函數Shape
,我們應該能夠傳遞類型的對象Circle
,Rectangle
或Square
而不影響函數的行為。
- 範例:考慮一個名為
- 介面隔離原則(ISP): 該原則指出,不應強迫客戶依賴他們不使用的方法。
- 例:考慮一個被呼叫的接口
PaymentMethod
用類似的方法pay
,refund
,而getTransactions
。我們可以為每個方法創建單獨的接口,並讓類別僅實現它們需要的接口,而不是將所有這些方法放在一個接口中。
- 例:考慮一個被呼叫的接口
Principio di inversione delle dipendenze
(蘸) :此原則規定高階模組不應依賴低階模組。兩者都應該依賴抽象。- 範例:考慮一個名為
Order
這取決於一個名為OrderRepository
。而不是直接實例化OrderRepository
inOrder
,我們可以使用依賴注入來注入一個實例OrderRepository
inOrder
.
- 範例:考慮一個名為
優點
透過遵循這些原則 SOLID in Laravel,我們可以編寫乾淨、可維護和可擴展的程式碼。
為了進一步說明如何在 Laravel 中實現 SOLID 原則,讓我們來看一個實際範例。
假設我們有一個允許用戶下產品訂單的應用程式。我們希望實現計算訂單總價並向客戶發送包含訂單詳細資訊的電子郵件的功能。我們可以透過遵循 SOLID 原則來實現這一目標。
單一職責原則 (SRP)
關注 SRP,我們可以建立單獨的類別來計算訂單的總價並向客戶發送電子郵件。例如:
class OrderCalculator
{
public function calculateTotal(Order $order): float
{
// Calculate total price of order
}
}
class OrderMailer
{
public function sendEmail(Order $order, User $user)
{
// Send email to customer with order details
}
}
開閉原理(OCP)
關注 OCP,我們可以使用服務容器和依賴注入 Laravel 創建一個靈活的系統,使我們能夠在不更改現有程式碼的情況下添加新功能。例如:
interface PaymentGateway
{
public function pay(Order $order): bool;
}
class PayPalGateway implements PaymentGateway
{
public function pay(Order $order): bool
{
// Process payment using PayPal API
}
}
class StripeGateway implements PaymentGateway
{
public function pay(Order $order): bool
{
// Process payment using Stripe API
}
}
class OrderProcessor
{
private PaymentGateway $gateway;
public function __construct(PaymentGateway $gateway)
{
$this->gateway = $gateway;
}
public function process(Order $order): void
{
// Process order using PaymentGateway
}
}
// In application service provider
$this->app->bind(PaymentGateway::class, StripeGateway::class);
在這裡,我們創建了 PaymentGateway
PayPal 和 Stripe 的一個介面和兩個實作。那麼讓我們創建一個類 OrderProcessor
這需要一個實例 PaymentGateway
透過其製造商。在應用服務提供者中,我們綁定介面 PaymentGateway
到實施 StripeGateway
,但我們可以輕鬆修改它以使用實現 PayPalGateway
必要的。
里氏替換原理 (LSP)
關注 LSP,第三個 紮實的原則,我們需要確保超類的任何子類都可以用來代替超類,而不影響程式的行為。在我們的範例中,我們可以透過使用類型提示和介面來確保這一點。例如:
interface OrderRepository
{
public function save(Order $order): void;
}
class DatabaseOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
// Save order to database
}
}
class InMemoryOrderRepository implements OrderRepository
{
public function save(Order $order): void
{
// Save order to in-memory cache
}
}
class OrderService
{
private OrderRepository $repository;
public function __construct(OrderRepository $repository)
{
$this->repository = $repository;
}
public function placeOrder(Order $order): void
{
// Place order and save to repository
}
}
// In application service provider
$this->app->bind(OrderRepository::class, DatabaseOrderRepository::class);
在這裡,我們創建了一個介面 OrderRepository
以及一個的兩個實現 數據庫 和記憶體快取。那麼讓我們創建一個類 OrderService
接受申請的 OrderRepository
透過其製造商。在應用服務提供者中,我們綁定介面 OrderRepository
到實施 DatabaseOrderRepository
,但我們可以輕鬆修改它以使用實現 InMemoryOrderRepository
如有必要,不影響程序的行為。
介面隔離原則(ISP)
要遵循ISP,第四個 紮實的原則,我們不應該強迫客戶依賴他們不使用的介面。在我們的範例中,我們可以透過創建更小、更集中的介面而不是大型的整體介面來確保這一點。例如:
interface OrderTotalCalculator
{
public function calculateTotal(Order $order): float;
}
interface OrderEmailSender
{
public function sendEmail(Order $order, User $user);
}
class OrderProcessor
{
private OrderTotalCalculator $calculator;
private OrderEmailSender $mailer;
public function __construct(OrderTotalCalculator $calculator, OrderEmailSender $mailer)
{
$this->calculator = $calculator;
$this->mailer = $mailer;
}
public function process(Order $order, User $user): void
{
$total = $this->calculator->calculateTotal($order);
$this->mailer->sendEmail($order, $user);
// Process order using total and mailer
}
}
在這裡,我們創建了兩個較小的接口,分別用於計算訂單的總價和發送電子郵件。那我們來修改一下類 OrderProcessor
取得這些介面的實例而不是單一大介面。這允許客戶端僅依賴他們需要的接口,而不是被迫依賴一個大型的整體接口。
依賴倒置原則(DIP)
為了遵循 DIP,我們應該依賴抽象而不是具體的實作。在我們的範例中,我們可以透過在整個程式碼中使用依賴注入和介面來實現這一點。例如:
interface PaymentGateway
{
public function pay(Order $order): bool;
}
interface OrderRepository
{
public function save(Order $order): void;
}
interface OrderTotalCalculator
{
public function calculateTotal(Order $order): float;
}
interface OrderEmailSender
{
public function sendEmail(Order $order, User $user);
}
class OrderProcessor
{
private PaymentGateway $gateway;
private OrderRepository $repository;
private OrderTotalCalculator $calculator;
private OrderEmailSender $mailer;
public function __construct(
PaymentGateway $gateway,
OrderRepository $repository,
OrderTotalCalculator $calculator,
OrderEmailSender $mailer
) {
$this->gateway = $gateway;
$this->repository = $repository;
$this->calculator = $calculator;
$this->mailer = $mailer;
}
public function process(Order $order, User $user): void
{
$total = $this->calculator->calculateTotal($order);
$this->mailer->sendEmail($order, $user);
$this->gateway->pay($order);
$this->repository->save($order);
// Process order using gateway, repository, calculator, and mailer
}
}
在這裡,我們為所有依賴項創建了介面並修改了類 OrderProcessor
透過其建構函式取得這些介面的實例。這使我們能夠在運行時輕鬆交換實現,並允許我們依賴抽象而不是具體實現。
總而言之,我們可以在 Laravel 中實現 SOLID 原則,透過使用依賴注入、介面和 Laravel 服務容器來建立一個靈活、可維護、易於修改和擴展的系統。