Parimet SOLID në Laravel: një shembull i plotë
SOLID është një grup parimesh për zhvillimin e softuerit të orientuar nga objekti.
Këto parime ndihmojnë për të siguruar që softueri të jetë i mirëmbajtur, i ripërdorshëm dhe i zgjerueshëm.
Në këtë artikull do të thellohemi më thellë në zbatimin e parimeve SOLID në Laravel
Koha e vlerësuar e leximit: 5 minuti
Parimet NGURTA ato janë:
- Parimi i Përgjegjësisë së Vetëm (SRP)
- Parimi i hapur-mbyllur (OCP)
- Parimi i Zëvendësimit të Liskov (LSP)
- Parimi i ndarjes së ndërfaqes (ISP)
- Parimi i përmbysjes së varësisë (DIP)
Si të zbatoni parimet SOLID në Laravel
- Parimi i Përgjegjësisë së Vetëm (SRP) Ky parim thotë se një klasë duhet të ketë vetëm një arsye për të ndryshuar. Me fjalë të tjera, një klasë duhet të ketë vetëm një përgjegjësi.
- Shembull: Konsideroni një klasë të quajtur
Order
. Në vend që të kemi të gjithë funksionalitetin që lidhet me një porosi në një klasë, ne mund ta ndajmë atë në klasa më të vogla si p.shOrderCalculator
,OrderRepository
DheOrderMailer
.
- Shembull: Konsideroni një klasë të quajtur
- Hapur-Mbyllur Parim (OCP) Ky parim thotë se një klasë duhet të jetë e hapur për zgjerim, por e mbyllur për modifikim. Me fjalë të tjera, ne duhet të jemi në gjendje të shtojmë veçori të reja pa ndryshuar kodin ekzistues.
- Shembull: Konsideroni një klasë të quajtur
PaymentGateway
. Në vend që ta modifikojmë këtë klasë sa herë që shtojmë një mënyrë të re pagese, ne mund të krijojmë një klasë të re për secilën mënyrë pagese që zgjeronPaymentGateway
klasës.
- Shembull: Konsideroni një klasë të quajtur
- Parimi i zëvendësimit të Liskov (LSP): ky parim përcakton se objektet e një superklase duhet të jenë në gjendje të zëvendësohen me objektet e një nënklase pa kompromentuar korrektësinë e programit.
- Shembull: Konsideroni një klasë të quajtur
Shape
me nënklasaCircle
,Rectangle
DheSquare
. Nëse kemi një funksion që pranon një objekt të tipitShape
, duhet të jemi në gjendje të kalojmë objekte të tipitCircle
,Rectangle
oseSquare
pa ndikuar në sjelljen e funksionit.
- Shembull: Konsideroni një klasë të quajtur
- Parimi i ndarjes së ndërfaqes (ISP): ky parim thotë se klientët nuk duhet të detyrohen të varen nga metodat që nuk përdorin.
- Shembull: Konsideroni një ndërfaqe të quajtur
PaymentMethod
me metoda sipay
,refund
DhegetTransactions
. Në vend që t'i kemi të gjitha këto metoda në një ndërfaqe të vetme, ne mund të krijojmë ndërfaqe të veçanta për secilën metodë dhe t'i bëjmë klasat të zbatojnë vetëm ndërfaqet që u nevojiten.
- Shembull: Konsideroni një ndërfaqe të quajtur
Principio di inversione delle dipendenze
(Zhyt) : Ky parim thotë se modulet e nivelit të lartë nuk duhet të varen nga modulet e nivelit të ulët. Të dyja duhet të varen nga abstraksionet.- Shembull: Konsideroni një klasë të quajtur
Order
e cila varet nga një klasë e quajturOrderRepository
. Në vend që të instantohen drejtpërdrejtOrderRepository
inOrder
, ne mund të përdorim injeksionin e varësisë për të injektuar një shembull tëOrderRepository
inOrder
.
- Shembull: Konsideroni një klasë të quajtur
përfitimet
Duke ndjekur këto parime NGURTA in Laravel, ne mund të shkruajmë kod të pastër, të mirëmbajtur dhe të zgjerueshëm.
Për të ilustruar më tej se si të arrihen parimet SOLID në Laravel, le të shohim një shembull praktik.
Supozoni se kemi një aplikacion që lejon përdoruesit të bëjnë porosi për produkte. Ne duam të zbatojmë funksionalitetin për të llogaritur çmimin total të një porosie dhe për t'i dërguar një email klientit me detajet e porosisë. Këtë mund ta arrijmë duke ndjekur parimet SOLID.
Parimi i Përgjegjësisë së Vetëm (SRP)
Për të ndjekur SRP, ne mund të krijojmë klasa të veçanta për të llogaritur çmimin total të një porosie dhe për t'i dërguar një email klientit. Për shembull:
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
}
}
Parimi i hapur-mbyllur (OCP)
Për të ndjekur OCP, ne mund të përdorim kontejnerin e shërbimit dhe injeksionin e varësisë së Laravel për të krijuar një sistem fleksibël që na lejon të shtojmë veçori të reja pa ndryshuar kodin ekzistues. Për shembull:
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);
Këtu, ne krijuam PaymentGateway
një ndërfaqe dhe dy implementime për PayPal dhe Stripe. Pra, le të krijojmë një klasë OrderProcessor
që merr një shembull PaymentGateway
përmes prodhuesit të tij. Në ofruesin e shërbimit të aplikacionit, ne lidhim ndërfaqen PaymentGateway
për zbatimin StripeGateway
, por ne mund ta modifikojmë lehtësisht për të përdorur zbatimin PayPalGateway
nëse është e nevojshme.
Parimi i Zëvendësimit të Liskov (LSP)
Për të ndjekur LSP, e treta e parime të forta, duhet të sigurohemi që çdo nënklasë e një superklase mund të përdoret në vend të superklasës pa ndikuar në sjelljen e programit. Në shembullin tonë, ne mund të sigurohemi për këtë duke përdorur sugjerime të tipit dhe ndërfaqe. Për shembull:
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);
Këtu, ne kemi krijuar një ndërfaqe OrderRepository
dhe dy zbatime për një bazës së të dhënave dhe një cache në memorie. Pra, le të krijojmë një klasë OrderService
që pranon një aplikim OrderRepository
përmes prodhuesit të tij. Në ofruesin e shërbimit të aplikacionit, ne lidhim ndërfaqen OrderRepository
për zbatimin DatabaseOrderRepository
, por ne mund ta modifikojmë lehtësisht për të përdorur zbatimin InMemoryOrderRepository
nëse është e nevojshme, pa ndikuar në sjelljen e programit.
Parimi i ndarjes së ndërfaqes (ISP)
Për të ndjekurISP, e katërta e parime të forta, ne nuk duhet t'i detyrojmë klientët të varen nga ndërfaqet që nuk përdorin. Në shembullin tonë, ne mund ta sigurojmë këtë duke krijuar ndërfaqe më të vogla, më të fokusuara në vend të ndërfaqeve të mëdha, monolitike. Për shembull:
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
}
}
Këtu, ne kemi krijuar dy ndërfaqe më të vogla për të llogaritur çmimin total të një porosie dhe për të dërguar një email, përkatësisht. Pra, le të modifikojmë klasën OrderProcessor
për të marrë shembuj të këtyre ndërfaqeve në vend të një ndërfaqeje të vetme, të madhe. Kjo i lejon klientët të varen vetëm nga ndërfaqet që u nevojiten, në vend që të detyrohen të varen nga një ndërfaqe e madhe monolitike.
Parimi i përmbysjes së varësisë (DIP)
Për të ndjekur DIP, ne duhet të mbështetemi në abstraksione në vend të zbatimeve konkrete. Në shembullin tonë, ne mund ta arrijmë këtë duke përdorur injeksionin e varësisë dhe ndërfaqet në të gjithë kodin tonë. Për shembull:
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
}
}
Këtu kemi krijuar ndërfaqe për të gjitha varësitë tona dhe kemi modifikuar klasën OrderProcessor
për të marrë shembuj të këtyre ndërfaqeve nëpërmjet konstruktorit të tij. Kjo na lejon të shkëmbejmë lehtësisht zbatimet në kohën e ekzekutimit dhe na lejon të varemi nga abstraksionet dhe jo nga zbatimet konkrete.
Në përmbledhje, ne mund të arrijmë parime SOLID në Laravel duke përdorur injeksionin e varësisë, ndërfaqet dhe kontejnerin e shërbimit Laravel për të krijuar një sistem fleksibël dhe të mirëmbajtur që është i lehtë për t'u modifikuar dhe zgjeruar.