若將商業(yè)邏輯都寫在controller谜嫉,會造成controller肥大而難以維護(hù),基于SOLID原則,我們應(yīng)該使用Service模式輔助controller,將相關(guān)的商業(yè)邏輯封裝在不同的service采桃,方便中大型項(xiàng)目的維護(hù)。
Version
Laravel 5.1.22
商業(yè)邏輯中丘损,常見的如:
牽涉到外部行為:如發(fā)送Email普办,使用外部API…。
使用PHP寫的邏輯:如根據(jù)購買的件數(shù)徘钥,有不同的折扣衔蹲。
若將商業(yè)邏輯寫在controller,會造成controller肥大呈础,日后難以維護(hù)舆驶。
牽涉到外部行為
如發(fā)送Email,初學(xué)者常會在controller直接調(diào)用Mail::queue():
publicfunctionstore(Request $request)
{
Mail::queue('email.index', $request->all(),function(Message $message){
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
? ? });
}
在中大型項(xiàng)目猪落,會有幾個問題:
將牽涉到外部行為的商業(yè)邏輯寫在controller贞远,造成controller的肥大難以維護(hù)畴博。
違反SOLID的單一職責(zé)原則:外部行為不應(yīng)該寫在controller笨忌。
controller直接相依于外部行為,使得我們無法對controller做單元測試俱病。
比較好的方式是使用service:
將外部行為注入到service官疲。
在service使用外部行為袱结。
將service注入到controller。
EmailService.php
app/Services/EmailService.php
namespaceApp\Services;
useIlluminate\Mail\Mailer;
useIlluminate\Mail\Message;
classEmailService
{
/**@varMailer */
private$mail;
/**
? ? * EmailService constructor.
*@paramMailer $mail
? ? */
publicfunction__construct(Mailer $mail)
? ? {
$this->mail = $mail;
? ? }
/**
? ? * 發(fā)送Email
*@paramarray $request
? ? */
publicfunctionsend(array $request)
? ? {
$this->mail->queue('email.index', $request,function(Message $message){
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
? ? ? ? });
? ? }
}
第 8 行
/**@varMailer */
private$mail;
/**
* EmailService constructor.
*@paramMailer $mail
*/
publicfunction__construct(Mailer $mail)
{
$this->mail = $mail;
}
將相依的Mailer注入到EmailService途凫。
20 行
/**
* 發(fā)送Email
*@paramarray $request
*/
publicfunctionsend(array $request)
{
$this->mail->queue('email.index', $request,function(Message $message){
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
? ? });
}
將發(fā)送Emai的商業(yè)邏輯寫在send()垢夹。
不是使用Mail facade,而是使用注入的$this->mail
UserController.php
app/Http/Controllers/UserController.php
namespaceApp\Http\Controllers;
useApp\Http\Requests;
useIlluminate\Http\Request;
useMyBlog\Services\EmailService;
classUserControllerextendsController
{
/**@varEmailService */
protected$emailService;
/**
? ? * UserController constructor.
*@paramEmailService $emailService
? ? */
publicfunction__construct(EmailService $emailService)
? ? {
$this->emailService = $emailService;
? ? }
/**
? ? * Store a newly created resource in storage.
*@param\Illuminate\Http\Request? $request
*@return\Illuminate\Http\Response
? ? */
publicfunctionstore(Request $request)
? ? {
$this->emailService->send($request->all());
? ? }
}
第9行
/**@varEmailService */
protected$emailService;
/**
* UserController constructor.
*@paramEmailService $emailService
*/
publicfunction__construct(EmailService $emailService)
{
$this->emailService = $emailService;
}
將相依的EmailService注入到UserController维费。
22行
/**
* Store a newly created resource in storage.
*@param\Illuminate\Http\Request? $request
*@return\Illuminate\Http\Response
*/
publicfunctionstore(Request $request)
{
$this->emailService->send($request->all());
}
從原本直接相依于Mail facade果元,改成相依于注入的EmailService
改用這種寫法,有幾個優(yōu)點(diǎn)
將外部行為寫在service犀盟,解決controller肥大問題而晒。
符合SOLID的單一職責(zé)原則:外部行為寫在service,沒寫在controller阅畴。
符合SOLID的依賴反轉(zhuǎn)原則:controller并非直接相依于service倡怎,而是將service依賴注入進(jìn)controller。
如根據(jù)購買的件數(shù)贱枣,有不同的折扣监署,初學(xué)者常會在controller直接寫if…else邏輯。
publicfunctionstore(Request $request)
{
$qty = $request->input('qty');
$price =500;
if($qty ==1) {
$discount =1.0;
}
elseif($qty ==2) {
$discount =0.9;
}
elseif($qty ==3) {
$discount =0.8;
}
else{
$discount =0.7;
}
$total = $price * $qty * $discount;
echo($total);
}
在中大型項(xiàng)目纽哥,會有幾個問題:
將PHP寫的商業(yè)邏輯直接寫在controller钠乏,造成controller的肥大難以維護(hù)。
違反SOLID的單一職責(zé)原則:商業(yè)邏輯不應(yīng)該寫在controller春塌。
違反SOLID的單一職責(zé)原則:若未來想要改變折扣與加總的算法缓熟,都需要改到此method,也就是說摔笤,此method同時包含了計(jì)算折扣與計(jì)算加總的職責(zé)够滑,因此違反SOLID的單一職責(zé)原則。
直接寫在controller的邏輯無法被其他controller使用吕世。
比較好的方式是使用service彰触。
將相依物件注入到service。
在service寫PHP邏輯使用相依物件命辖。
將service注入到controller况毅。
OrderService.php
app/Services/OrderService.php
namespaceApp\Services;
classOrderService
{
/**
? ? * 計(jì)算折扣
*@paramint $qty
*@returnfloat
? ? */
publicfunctiongetDiscount($qty)
? ? {
if($qty ==1) {
return1.0;
}elseif($qty ==2) {
return0.9;
}elseif($qty ==3) {
return0.8;
}else{
return0.7;
? ? ? ? }
? ? }
/**
? ? * 計(jì)算最後價錢
*@paraminteger $qty
*@paramfloat $discount
*@returnfloat
? ? */
publicfunctiongetTotal($qty, $discount)
? ? {
return500* $qty * $discount;
? ? }
}
第 5 行
/**
* 計(jì)算折扣
*@paramint $qty
*@returnfloat
*/
publicfunctiongetDiscount($qty)
{
if($qty ==1) {
return1.0;
}elseif($qty ==2) {
return0.9;
}elseif($qty ==3) {
return0.8;
}else{
return0.7;
? ? }
}
為了符合SOLID的單一職責(zé)原則,將計(jì)算折扣獨(dú)立成getDiscount()尔艇,將PHP寫的判斷邏輯寫在里面尔许。
23行
/**
* 計(jì)算最後價錢
*@paramint $qty
*@paramfloat $discount
*@returnfloat
*/
publicfunctiongetTotal($qty, $discount)
{
return500* $qty * $discount;
}
為了符合SOLID的單一職責(zé)原則,將計(jì)算加總獨(dú)立成getTotal()终娃,將PHP寫的計(jì)算邏輯寫在里面味廊。
OrderController.php
app/Http/Controllers/OrderController.php
namespaceApp\Http\Controllers;
useApp\Http\Requests;
useApp\MyBlog\Services\OrderService;
useIlluminate\Http\Request;
classOrderControllerextendsController
{
/**@varOrderService */
protected$orderService;
/**
? ? * OrderController constructor.
*@paramOrderService $orderService
? ? */
publicfunction__construct(OrderService $orderService)
? ? {
$this->orderService = $orderService;
? ? }
/**
? ? * Store a newly created resource in storage.
*@param\Illuminate\Http\Request? $request
*@return\Illuminate\Http\Response
? ? */
publicfunctionstore(Request $request)
? ? {
$qty = $request->input('qty');
$discount =$this->orderService->getDiscount($qty);
$total =$this->orderService->getTotal($qty, $discount);
echo($total);
? ? }
}
第 9 行
/**@varOrderService */
protected$orderService;
/**
* OrderController constructor.
*@paramOrderService $orderService
*/
publicfunction__construct(OrderService $orderService)
{
$this->orderService = $orderService;
}
將相依的OrderService注入到UserController。
21行
/**
* Store a newly created resource in storage.
*@param\Illuminate\Http\Request? $request
*@return\Illuminate\Http\Response
*/
publicfunctionstore(Request $request)
{
$qty = $request->input('qty');
$discount =$this->orderService->getDiscount($qty);
$total =$this->orderService->getTotal($qty, $discount);
echo($total);
}
將原本的if…else邏輯改成呼叫OrderService,controller變得非常干凈余佛,也達(dá)成原本controller接收HTTP request柠新,調(diào)用其他class的責(zé)任。
改用這種寫法辉巡,有幾個優(yōu)點(diǎn):
將PHP寫的商業(yè)邏輯寫在service恨憎,解決controller肥大問題。
符合SOLID的單一職責(zé)原則:商業(yè)邏輯寫在service郊楣,沒寫在controller憔恳。
符合SOLID的單一職責(zé)原則:計(jì)算折扣與計(jì)算加總分開在不同method,且歸屬于OrderService净蚤,而非OrderController喇嘱。
符合SOLID的依賴反轉(zhuǎn)原則:controller并非直接相依于service,而是將service依賴注入進(jìn)controller塞栅。
其他controller也可以重復(fù)使用此段商業(yè)邏輯者铜。
Controller
牽涉到外部行為
publicfunctionstore(Request $request)
{
$this->emailService->send($request->all());
}
使用PHP寫的邏輯
publicfunctionstore(Request $request)
{
$qty = $request->input('qty');
$discount =$this->orderService->getDiscount($qty);
$total =$this->orderService->getTotal($qty, $discount);
echo($total);
}
若使用了service輔助controller,再搭配依賴注入與service container放椰,則controller就非常干凈作烟,能專心處理接收HTTP request,調(diào)用其他class的職責(zé)了砾医。
Conclusion
實(shí)務(wù)上會有很多service拿撩,須自行依照SOLID原則去判斷是否該建立service。
Service使得商業(yè)邏輯從controller中解放如蚜,不僅更容易維護(hù)压恒、更容易擴(kuò)展、更容易重復(fù)使用错邦,且更容易測試探赫。