上篇介紹完SOLID,本想接著介紹Service Container & Service Provider
但想想,一直在理論方面可能太過乏味(絕對不是筆者想偷懶
Service 和 Repository 分別有不同的定義,要不要用取決於個人及專案,也可以只用其中一個
如果兩者同時存在,順序一般是
Controller → Service → Repository
以先前的PostController:index為例:
先以API測試
這是原始測試結果
app\Repositories\PostRepository.php
<?php
namespace App\Repositories;
use App\Models\Post;
use Illuminate\Support\Collection;
class PostRepository
{
public function get() : Collection
{
return Post::orderBy('id', 'desc')->get();
}
}
app\Services\PostService.php
請手動建立檔案及資料夾
<?php
namespace App\Services;
use App\Repositories\PostRepository;
use Illuminate\Support\Collection;
class PostService
{
public function get(): Collection
{
$postRepository = new PostRepository;
return $postRepository->get();
}
}
引用PostService()
use App\Services\PostService;
public function index() {
// $posts = Post::all();
// return view('posts.index', ['posts' => $posts]);
$postService = new PostService();
$posts = $postService->get();
return response()->json(['data' => $posts], 200);
}
測試結果(排序變了):
看起來各個模組已經完成了,但是聰明的你可能看出來了
$postService = new PostService();
$postRepository = new PostRepository;
這兩行並不符合上篇提到的依賴反轉原則
因為new PostService()已經將其實例化,而不是使用它的抽象
還記得檯燈和按鈕的例子嗎,PostRepository在這裡是檯燈,PostService是按鈕
所以PostService不該實例化PostRepository,而是依靠依賴注入
我們先把程式碼修改一下,再來討論問題
透過Laravel的依賴注入,注入(inject)了一個反射(reflection: PostService or PostRepository)給$postService
PostController
public function index(PostService $postService) {
// $posts = Post::all();
// return view('posts.index', ['posts' => $posts]);
// $postService = new PostService();
$posts = $postService->get();
return response()->json(['data' => $posts], 200);
}
PostService
<?php
namespace App\Services;
use App\Repositories\PostRepository;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class PostService
{
private $postRepository;
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function get(): array
{
try {
return $this->postRepository->get()->toArray();
} catch (\Throwable $th) {
Log::info($th);
return [];
}
}
}
這邊增加了try catch,改變回傳格式,演示Service的角色定位
PostRepository
<?php
namespace App\Repositories;
use App\Models\Post;
use Illuminate\Database\Eloquent\Collection;
class PostRepository
{
public function getOrderByID() : Collection
{
return Post::orderBy('id', 'desc')->get();
}
}
以這個範例來說,程式碼確實都能正常運作,沒有問題
如果只說這違反了依賴反轉原則,筆者自己也無法接受
問了幾次ChatGPT也是一直鬼打牆
以下就筆者自己的親身經驗及心得:
假設今天PostService中使用五個Repositories
你可以在任何地方new Repository()沒錯
但是今天新來一個技術經理說:「這個專案耦合性太高了,必須解耦」
眾人:「???耦合是甚麼,能吃嗎」
剛好那個技術經理叫做Ryan(筆者技術經理:「給我解就對了!」
技術經理:「(拿了自己的技術文章給各位看,誇讚自己好棒棒),現在懂了吧,給我解!」
OK,問題來了
所有用new PostService()、Repository()的地方通通要改成
public function __construct(PostRepository $postRepository, UserRepository $userRepository)
{
$this->postRepository = $postRepository;
$this->userRepository = $userRepository;
}
就頭痛了
那你可能說,我們專案就打死都不碰依賴注入,可行嗎?
Maybe、Maybe not,取決於專案是否有一天會遇到,測試、維護、介面化的痛點
可以說是與其花時間做、遵循規範,避免未來的麻煩
百利而無一害?
SOLID痛點
本來想偷懶,結果又寫了幾個小時
主要是筆者不想都是以這是理論、規則就是這樣的方式說服讀者
過去也是這樣說服自己,這是理論,記住、會用就好
但心中就是有個聲音:別告訴我為甚麼,告訴我好和不好的例子
Anyway,今天介紹手動新增Service、Repository,以及依賴注入到高層組件使用
下篇真的會接著介紹Service Container,繼續深入依賴注入、依賴反轉的課題
也許會更進一步了解到解耦的重要性