如何使用 PhpStorm 重構成 Closure?
在實務上,我們常常會遇到將重複程式碼重構成 closure 的需求,PhpStorm 沒辦法直接重構,需透過一些技巧。
Motivation
以前一直不知道如何將 closure 用在自己的程式上,一直到看了 Refactoring to Collection 之後,整理了一篇實務上如何活用 Closure?,不過當時仍然不知道要如何在 PhpStorm 完成,都是手動複製貼上,本文以 PhpStorm 的 Extract Method
、Extract Parameter
與 Surround With
,將重複的程式碼重構成 closure。
Version
PHP 7.0.0
Laravel 5.2.39
PhpStorm 2016.1.2
實際案例
PostService.php 1 1GitHub Commit : 新增 displayAllPost()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35namespace App\Services;
use App\Repositories\PostRepository;
class PostService
{
/**
* @var PostRepository
*/
private $postRepository;
/**
* PostService constructor.
* @param PostRepository $postRepository
*/
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
/**
* @return int
*/
public function displayAllPosts()
{
$posts = $this->postRepository->getAllPosts();
foreach ($posts as $post) {
$txt = "{$post->id} : {$post->title}" . PHP_EOL;
echo($txt);
}
return $posts->count();
}
}
21 行1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* @return int
*/
public function displayAllPosts()
{
$posts = $this->postRepository->getAllPosts();
foreach ($posts as $post) {
$txt = "{$post->id} : {$post->title}" . PHP_EOL;
echo($txt);
}
return $posts->count();
}
若我們發現以下程式碼是重複的,想將其提出成為一個 method1
2
3
4
5
6
7$posts = $this->postRepository->getAllPosts();
foreach ($posts as $post) {
}
return $posts->count();
也就是除了1
2$txt = "{$post->id} : {$post->title}" . PHP_EOL;
echo($txt);
之外,其他都是重複程式碼,想 Extract Method
。
Extract Method
PhpStorm 內建的 Extract Method
無法簡單的萃取出這段程式碼,需靠一點技巧。
先選擇全部程式碼,按熱鍵 ⌃ + T,顯示 Refactor This
選單,選擇 Extract Method
。
輸入我們要重構的新 method 名稱。
PhpStorm 會幫我們選擇的程式碼重構成 loopAllPosts()
,並且自動加上 return $this->loopAllPosts()
。
但是這樣還沒完,因為1
2$txt = "{$post->id} : {$post->title}" . PHP_EOL;
echo($txt);
並不是我們要的,而是希望由 closure 傳入的,所以我們必須將這段程式碼 Extract Parameter
。
Extract Parameter
不過 PhpStorm 也沒有辦法簡單的的將一段程式碼 Extract Parameter
。
還記得國中幾何嗎? 我們常常需要加上輔助線
才能證明,我們來替這段程式碼加上輔助線。
因為 PhpStorm 只能將一個字串或一個數字使用 Extract Parameter
,而無法將一段程式碼去 Extract Parameter
,因此我們將我們要萃取的程式碼先用單引號
括起來,騙 PhpStorm 這是一個字串
。
將滑鼠游標放在假字串
內,按熱鍵 ⌃ + T,顯示 Refactor This
選單,選擇 Extract Parameter
。
輸入 paramter 名稱。
PhpStorm 或幫我們將假字串
以 $closure
變數取代,並將假字串
搬到loopAllPosts()
的參數。
將 $post
傳入 closure,這個 PhpStorm 沒辦法幫我們做,必須手動加上。
這個假字串
很像我們的 closure,但仍然不是,我們需要繼續重構。
Surround With
一樣重施輔助線
技巧,PhpStorm 無法將字串重構成 function,需加上 return
,騙 PhpStorm 可以將此段程式碼重構。
選擇 return
與 假字串
,按熱鍵 ⌥ + ⌘ + T,出現 Surround With
對話框,選擇 function
。2 2選擇部分,可將滑鼠游標放在假字串
內,按熱鍵 ⌥ + ↑ 數次,直到選到你要的部分為止。
PhpStorm 會幫我們加上 function
關鍵字,已經很接近 closure 了。
手動將剛剛的輔助線
全部刪除,並加上 $post
參數。
按熱鍵 ⌥ + ⌘ + T,PhpStorm 會將我們將程式重新 format 成漂漂亮亮符合 PSR-2
格式的程式碼。
PostService.php 3 3GitHub Commit : 重構成 Closure1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39namespace App\Services;
use App\Repositories\PostRepository;
class PostService
{
/**
* @var PostRepository
*/
private $postRepository;
/**
* PostService constructor.
* @param PostRepository $postRepository
*/
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function displayAllPosts()
{
return $this->loopAllPosts(function (Post $post) {
$txt = "{$post->id} : {$post->title}" . PHP_EOL;
echo($txt);
});
}
private function loopAllPosts(Closure $closure)
{
$posts = $this->postRepository->getAllPosts();
foreach ($posts as $post) {
$closure($post);
}
return $posts->count();
}
}
單元測試
馬上跑單元測試,綠燈 打完收工。
PostServiceTest.php 4 4GitHub Commit : 單元測試 : 顯示所有 Post()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18use App\Services\PostService;
class PostServiceTest extends TestCase
{
/** @test */
public function 顯示所有Post()
{
/** arrange */
$expected = 10;
$target = App::make(PostService::class);
/** act */
$actual = $target->displayAllPosts();
/** assert */
$this->assertEquals($expected, $actual);
}
}
Summary
- 將全部程式碼以
Extract Method
提煉成新的 method。 - 將不同的程式碼加上單引號變成字串,使用
Extract Parameter
提煉參數。 - 將
假字串
使用Surround With
變成 closure。 - 將
輔助線
刪除。 - 跑單元測試確認重構成功。
Conclusion
- PhpStorm 內建的
Extract Method
與Extract Parameter
,搭配輔助線
的小技巧,一樣的可以快速地重構出 closure。
Sample Code
完整的範例可以在我的 GitHub 上找到。