責任鏈設計模式 (Chain of Responsibility)

Jc
9 min readOct 9, 2023

Hi 👋 ~ 今天來跟大家聊聊責任鏈設計模式 (Chain of Responsibility Design Pattern)!

責任鏈設計模式是什麼?

  • 責任鏈模式是一種行爲設計模式。
  • 針對一個處理的請求,在一連串由不同節點組成的流程中,每一個節點可以決定是否進行處理、傳遞給下一個節點或判斷不需要往下處理而駁回請求。

沒有使用責任鏈模式會怎麼樣嗎?🤯

我想大家都有去面試過的經驗,不少公司的面試流程很多都需要有 3–4 次面試 🥲

我們這次就用面試流程來看看如何使用責任鏈模式吧!

背景設定

JC KeyCode 是一家新創公司,它需要應徵「軟體工程師」與「設計師」。

針對「軟體工程師」的面試流程是:

  1. HR 電話初步面試
  2. 線上技術解題面試
  3. 到公司與 CTO 技術解題面試
  4. 到公司與 CEO 的 1 on 1 面試

針對「設計師」的面試流程是:

  1. HR 電話初步面試
  2. 到公司與設計總監進行設計作品集面試
  3. 到公司與 CEO 的 1 on 1 面試

沒有使用責任鏈設計模式的程式碼

我們會先有應徵者的 class:

const POSITION_TYPE_ENGINEER = 'enginner';
const POSITION_TYPE_DESIGNER = 'designer';

class Interviewee
{
public string $name;
public string $position;
public int $performance;

public function __construct(string $name, string $position, int $performance)
{
$this->name = $name;
$this->position = $position;
$this->performance = $performance;
}
}

然後就是 JcKeyCode 這家公司的 class:

class JcKeyCode
{

public function interview(Interviewee $interviewee): bool
{
// Todo:面試流程 ...
}

public function hire(Interviewee $interviewee)
{
$position = $interviewee->position == POSITION_TYPE_ENGINEER ? "工程師" : "設計師";
echo "{$interviewee->name} - {$position} 面試流程開始: ==================== " . PHP_EOL;
$result = $this->interview($interviewee);
if ($result) {
$message = "get the offer ! 🎉";
} else {
$message = "interview failed 😭";
}
echo "{$interviewee->name} - {$position} 面試流程結束: {$message} ==================== " . PHP_EOL;
echo "" . PHP_EOL;
}
}

接下來實作 interview 這個 method 的內容:

public function interview(Interviewee $interviewee): bool
{
echo "HR 電話初步面試: Start" . PHP_EOL;
// HR 電話初步面試
if ($interviewee->performance > 5) {
echo "HR 電話初步面試: Pass" . PHP_EOL;
// if Engineer
if ($interviewee->position == POSITION_TYPE_ENGINEER) {
// 線上技術解題面試
echo "線上技術解題面試: Start" . PHP_EOL;
if ($interviewee->performance > 30) {
echo "線上技術解題面試: Pass" . PHP_EOL;
// 到公司與 CTO 技術解題面試
echo "到公司與 CTO 技術解題面試: Start" . PHP_EOL;
if ($interviewee->performance > 70) {
echo "到公司與 CTO 技術解題面試: Pass" . PHP_EOL;
// 到公司與 CEO 的 1 on 1 面試
echo "到公司與 CEO 的 1 on 1 面試: Start" . PHP_EOL;
if ($interviewee->performance > 90) {
echo "到公司與 CEO 的 1 on 1 面試: Pass" . PHP_EOL;
return true;
} else {
echo "到公司與 CEO 的 1 on 1 面試: Failed" . PHP_EOL;
return false;
}
} else {
echo "到公司與 CTO 技術解題面試: Failed" . PHP_EOL;
return false;
}
} else {
echo "線上技術解題面試: Failed" . PHP_EOL;
return false;
}
}

// if Designer
if ($interviewee->position == POSITION_TYPE_DESIGNER) {
// 到公司與設計總監進行設計作品集面試
echo "到公司與設計總監進行設計作品集面試: Start" . PHP_EOL;
if ($interviewee->performance > 80) {
echo "到公司與設計總監進行設計作品集面試: Pass" . PHP_EOL;
// 到公司與 CEO 的 1 on 1 面試
echo "到公司與 CEO 的 1 on 1 面試: Start" . PHP_EOL;
if ($interviewee->performance > 90) {
echo "到公司與 CEO 的 1 on 1 面試: Pass" . PHP_EOL;
return true;
} else {
echo "到公司與 CEO 的 1 on 1 面試: Failed" . PHP_EOL;
return false;
}
} else {
echo "到公司與設計總監進行設計作品集面試: Failed" . PHP_EOL;
return false;
}
}
} else {
echo "HR 電話初步面試: Failed" . PHP_EOL;
return false;
}
}

最後使用的程式碼:

function main()
{
$jcKeyCode = new JcKeyCode();
// deisgner
$john = new Interviewee("John", POSITION_TYPE_DESIGNER, 3);
$jcKeyCode->hire($john);
$alice = new Interviewee("Alice", POSITION_TYPE_DESIGNER, 15);
$jcKeyCode->hire($alice);
$paul = new Interviewee("Paul", POSITION_TYPE_DESIGNER, 85);
$jcKeyCode->hire($paul);
$patty = new Interviewee("Patty", POSITION_TYPE_DESIGNER, 95);
$jcKeyCode->hire($patty);

// engineer
$mary = new Interviewee("Mary", POSITION_TYPE_ENGINEER, 4);
$jcKeyCode->hire($mary);
$tommy = new Interviewee("Tommy", POSITION_TYPE_ENGINEER, 15);
$jcKeyCode->hire($tommy);
$peter = new Interviewee("Peter", POSITION_TYPE_ENGINEER, 80);
$jcKeyCode->hire($peter);
$david = new Interviewee("David", POSITION_TYPE_ENGINEER, 99);
$jcKeyCode->hire($david);
}

main();

🤯 上述程式碼的問題:

  1. interveiw() 裏面 ifelse 的複雜的判斷邏輯混雜在一起
  2. interveiw() 裏面的判斷式無法共用,例如 engineerdesigner 最後都需要經過 CEO 的面試,但卻要因爲不同的狀況要分別寫在兩處
  3. interveiw() 裏面已經這麼混亂的情況下,如果要再新增一個 HR 的職位面試判斷,想必是一場災難。。。 🤯

例如: HR 只需要給 CEO 面試過關即可。

public function interview(Interviewee $interviewee): bool
{

echo "HR 電話初步面試: Start" . PHP_EOL;
// HR 電話初步面試
if ($interviewee->performance > 5) {
echo "HR 電話初步面試: Pass" . PHP_EOL;
// if Engineer
if ($interviewee->position == POSITION_TYPE_ENGINEER) {
// 工程師的面試流程 ... 略
}
// if Designer
if ($interviewee->position == POSITION_TYPE_DESIGNER) {
// 設計師的面試流程 ... 略
}
// if HR
if ($interviewee->position == POSITION_TYPE_HR) {
// 到公司與 CEO 的 1 on 1 面試
echo "到公司與 CEO 的 1 on 1 面試: Start" . PHP_EOL;
if ($interviewee->performance > 90) {
echo "到公司與 CEO 的 1 on 1 面試: Pass" . PHP_EOL;
return true;
} else {
echo "到公司與 CEO 的 1 on 1 面試: Failed" . PHP_EOL;
return false;
}
}
} else {
echo "HR 電話初步面試: Failed" . PHP_EOL;
return false;
}
}

相信你看到上述的實作應該覺得有點不妙。。。 🥲

那些接下來我們前往 圈圈工程師 就來看看如何使用責任鏈設計模式改善上述的程式碼吧!

--

--