透過輔助線重構成 Closure

在實務上,我們常常會遇到將重複程式碼重構成 closure 的需求,PhpStorm 沒辦法直接重構,需透過一些技巧。

Motivation


以前一直不知道如何將 closure 用在自己的程式上,一直到看了 Refactoring to Collection 之後,整理了一篇實務上如何活用 Closure?,不過當時仍然不知道要如何在 PhpStorm 完成,都是手動複製貼上,本文以 PhpStorm 的 Extract MethodExtract ParameterSurround With ,將重複的程式碼重構成 closure。

Version


PHP 7.0.0
Laravel 5.2.39
PhpStorm 2016.1.2

實際案例


PostService.php 1 1GitHub Commit : 新增 displayAllPost()

app/Services/PostService.php
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
35
namespace 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();
}

若我們發現以下程式碼是重複的,想將其提出成為一個 method

1
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 對話框,選擇 function2 2選擇部分,可將滑鼠游標放在假字串內,按熱鍵 ⌥ + ↑ 數次,直到選到你要的部分為止。

PhpStorm 會幫我們加上 function 關鍵字,已經很接近 closure 了。

手動將剛剛的輔助線全部刪除,並加上 $post 參數。

按熱鍵 ⌥ + ⌘ + T,PhpStorm 會將我們將程式重新 format 成漂漂亮亮符合 PSR-2 格式的程式碼。

PostService.php 3 3GitHub Commit : 重構成 Closure

app/Services/PostService.php
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
35
36
37
38
39
namespace 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()

app/Services/PostServiceTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use 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


  1. 將全部程式碼以 Extract Method 提煉成新的 method。
  2. 將不同的程式碼加上單引號變成字串,使用 Extract Parameter 提煉參數。
  3. 假字串使用 Surround With 變成 closure。
  4. 輔助線刪除。
  5. 跑單元測試確認重構成功。

Conclusion


  • PhpStorm 內建的 Extract MethodExtract Parameter,搭配輔助線的小技巧,一樣的可以快速地重構出 closure。

Sample Code


完整的範例可以在我的 GitHub 上找到。