# Uncle Bob - Naming

การทำ Clean Code ตอนที่ 2.2 จากวีดีโอของ 🧓 **ลุงบ๊อบ** หรือ [**Robert C. Martin**](https://en.wikipedia.org/wiki/Robert_C._Martin) หนึ่งในมหาเทพวงการคอมพิวเตอร์ ซึ่งในรอบนี้เราจะมาดูกันว่า ป๋าแกมีมุมมองใน **ตัวชื่อของต่างๆ** ยังไงกันบ้างถึงจะคลีนกันฮ๊าฟ 😘 ส่วนใครที่ยังไม่ได้อ่านตอนก่อนหน้าก็กดอ่านได้จากลิงค์นี้เบย [🧓 **Uncle Bob - Part 2.1**](https://www.saladpuk.com/basic/clean-code/uncle-bob-part-2)

{% hint style="success" %}
**แนะนำให้อ่าน**\
บทความนี้เป็นส่วนหนึ่งของบทคอร์ส [👶 **Clean Code**](https://www.saladpuk.com/basic/clean-code) หากเพื่อนๆแมวน้ำสนใจศึกษาเรียนรู้ว่าการทำ Clean Code ว่ามันคืออะไร? มีอะไรบ้าง? บลาๆ ก็สามารถกดที่ชื่อบทความสีฟ้าๆเข้าไปอ่านได้เลยครัช
{% endhint %}

## 🧐 ไฟล์ในโปรเจค

### 📄 จำนวนบรรทัด

ลุงบ๊อบได้ลองเอาโปรเจคของแกมา 7 ตัว แล้วเอามาวิเคราะห์หาความสัมพันธต่างๆดู แล้วก็พบจุดที่น่าสนใจที่เกี่ยวกับ **ไฟล์ในโปรเจค** ตามรูปด้านล่าง

![จำนวนบรรทัดต่อ 1 ไฟล์ในโปรเจค](/files/-MEgVB7ulSHbGL4KFq-E)

จากรูปด้านบนมันแสดงให้เห็นว่า **ไม่ว่าโปรเจคจะง่ายหรือยากก็ตาม ขนาดไฟล์ไม่ได้มีผลเลย** ซึ่งหมายความว่า โปรเจคที่เราได้รับมา เราจะเขียนโค้ดเยอะๆหรือน้อยๆก็ได้ ตราบใดที่มันทำงานได้

> 🧓 ขนาดไฟล์และจำนวนบรรทัด - จะมากหรือน้อยมันขึ้นอยู่กับ รูปแบบในการเขียนโค้ดที่ทีมจะเลือกใช้ (Coding Standard) ก็แค่นั้นแหละ

{% hint style="info" %}
**Coding Standard**\
เป็นมาตรฐานที่บังคับให็ทุกคนในทีมต้องทำตามเวลาเขียนโค้ด เพราะทุกคนจะได้เขียนของต่างๆออกมาเหมือนๆกัน เช่น รูปแบบการตั้งชื่อ รูปแบบการคอมเมนต์ รูปแบบการจัดการไฟล์ บลาๆ ซึ่งถามว่าไม่ใช้ได้ป่ะ? คำตอบคือได้นะ แต่ลองจินตนาการว่าเราต้องไปแก้โค้ดของคนอื่น แล้วต่างคนต่างเขียน ต่างคนก็มีสไตล์เป็นของตัวเอง แล้วเรายังอยากจะไปอ่านโค้ดที่มี 3-4 สไตล์ป่ะ? นั่นแหละข้อเสียของการไม่มี Coding Standard
{% endhint %}

### 🌭 จำนวนตัวอักษร

ถัดมาลุงแกก็ค้นพบว่า โปรเจคทั้ง 7 ตัวที่ไม่เกี่ยวข้องอะไรกันแต่กลับมี **จำนวนตัวอักษรต่อ 1 บรรทัด** ใกล้เคียงกันมาก ตามรูปด้านล่าง

![จำนวนตัวอักษรต่อ 1 บรรทัด](/files/-MEg_P8lwJMgeZ9SeL3n)

จากรูปด้านบนมันแสดงให้เห็นว่า **ไม่มีคนชอบโค้ดยาวๆ** และจุดที่เหมาะสมในการเขียนโค้ดควร **ไม่เกิน 30-40 ตัวอักษร** (เราไม่ได้เขียนโค้ดภาษาไทย ดังนั้นอ้างไม่ได้นะเฟร้ย)

> 🧓 ขนาดหน้าจอไม่มีผล เพราะต่อให้เป็นหน้าจอยาวแค่ไหนก็ตาม เราก็ไม่ชอบโค้ดยาวๆ และ สำหรับจอปรกติ เราก็ไม่ชอบเลื่อนจอซ้ายขวากลับไปกลับมาอยู่ดี และการทำ Wordwrap ก็ยิ่งน่าเกลียด

## 🏷️ การตั้งชื่อ

**ชื่อเกี่ยวข้องกับทุกอย่างในโปรเจค** ไม่ว่าจะเป็น ชื่อโปรเจค ชื่อโฟเดอร์ ชื่อไฟล์ ชื่อคลาส ชื่อเมธอด ชื่อตัวแปร บลาๆ ซึ่ง 💖 **หนึ่งในหัวใจหลักของการทำ Clean Code คือการตั้งชื่อ** นี่แหละ

> 🧓 ถ้าเราไม่ใส่ใจในการตั้งชื่อ เราจะไม่สามารถเขียนโค้ดที่อธิบายตัวมันเองได้เลย

### ❌ อย่าตั้งชื่อคลุมเครือ

อะไรก็ตามที่ไม่ชัดเจน เราก็ต้องไปใส่คอมเมนต์อธิบายมันต่ออะดิ ตามโค้ดด้านล่าง

```csharp
int d; // elapsed time in days
```

### 🔹 ตัวแปรต่างๆ

การตั้งชื่อตัวแปรให้ **ดูที่ Scope ของมัน** ซึ่งถ้า **Scope สั้นให้ตั้งชื่อสั้น** แต่ถ้า **Scope ยาวให้ตั้งชื่อยาว** ตามตัวอย่างด้านล่าง

{% hint style="info" %}
**อธิบายเพิ่มเติม**\
Scope ในกรณี้นี้หมายถึง **Life Cycle ของสิ่งนั้นๆ** โดยปรกติของทุกอย่างมันจะมีชีวิตอยู่ได้แค่ภายในวงเล็บปีกกา { } เท่านั้น ซึ่งมันมีชีวิตอยู่มากหรือน้อย ขึ้นอยู่กับว่ายังมีใครใช้หรืออ้างอิงมันอยู่หรือเปล่า ซึ่งถ้าไม่มีแล้ว โดยปรกติเดี๋ยวมันก็จะมีตัว **Garbage collector** มาคอยทำลายของพวกนั้นทิ้ง เพื่อคืนหน่วยความจำนั่นเอง
{% endhint %}

#### **Short Scope**

ถ้าเราต้องการสร้างตัวแปรมารับค่าซักอย่าง ซึ่งมันจะถูกใช้งานแค่ไม่กี่บรรทัดเท่านั้น หรือพูดง่ายๆคือ scope หรือ life cycle มันน้อย เราก็จะตั้งชื่อสั้นๆได้ เพราะจุดที่เรียกใช้มันอยู่ใกล้ๆกับตัวแปรนั้นเรายังจำที่มาที่ไปของมันได้อยู่

```csharp
var input = Console.ReadLine();
if(input == "something")
{
    // ทำไรซักอย่าง
}
```

ตัวอย่างตัวแปรที่จบภายในบรรทัดเดียว หรือที่เราเรียกกันว่า inline variable ลุงแกจะตั้งชื่อแค่ 1 ตัวอักษรยังได้เลย

```csharp
return int.TryParse("1", out int v)? v : 0;
```

#### Long Scope

ถ้าเราต้องสร้างตัวแปรมารับค่าซักอย่าง ซึ่งมันจะถูกเอาไปใช้งานในบรรทัดไกลๆ หรือ ภายในเมธอดอื่น เราก็ต้องตั้งชื่อมันให้ยาวขึ้น เพราะชื่อมันต้องชัดเจนว่ามันคืออะไร เพราะในจุดที่เรียกใช้ตัวแปรตัวนั้นเราอาจจะจำไม่ได้แล้วว่าตัวแปรนี้มีที่มาที่ไปยังไง

```csharp
var rawMoneyText = Console.ReadLine();
if(string.IsNullOrWhiteSpace(rawMoneyText))
{
    throw new ArgumentNullException();
}
// ผ่านไปอีกซัก 10 บรรทัด
if(int.TryParse(rawMoneyText, out int money))
{
    // ทำไรซักอย่าง
}
```

> 🧓 สาเหตุที่ลุงให้ตั้งชื่อตาม scope เพราะตอนที่เรียกใช้ตัวแปร เราต้องชัดเจนว่าตัวแปรนั้นคืออะไร ดังนั้นยิ่งมันมีชีวิตอยู่ยาว นั่นหมายความว่าในจุดที่โค้ดเรียกใช้ตัวแปรนั้น เรามีโอกาสสูงมากที่จะลืมที่มาที่ไปของตัวแปรพวกนั้นแล้ว ดังนั้นถ้าชื่อตัวแปรมันบอกเจตนาของมันว่า **มันใช้เก็บอะไร** เราก็จะไม่พลาดในตอนที่เรียกใช้ยังไงล่ะ

### 🔹 คลาส & เมธอด

ของพวกนี้จะ **ตรงข้ามกับตัวแปร** เพราะถ้า **Scope สั้นให้ตั้วชื่อยาว** แต่ถ้า **Scope ยาวให้ตั้งชื่อสั้น** และ เราควรจะ **ตั้งชื่อเป็น High Level & Abstraction** ด้วย โดยลุงแกให้เหตุผลที่ทำแบบนั้นว่า

{% hint style="info" %}
**เกร็ดความรู้**\
**High Level & Abstraction** หมายถึง เราจะตั้งชื่อเป็น การทำงานระดับบน ไม่ต้องอธิบายว่าจริงๆมันทำงานยังไง เพราะ เวลาอ่านเราจะได้เข้าใจไอเดียว่าของพวกนี้ใช้ทำอะไร โดยไม่ต้องไปสนใจรายละเอียดการทำงานจริงๆของมัน เช่น CreateUser หากเป็น Low Level มันจะเขียนออกมาประมาณว่า InsertNewUserToMongoDB ไรงี้ ซึ่งมันเป็นการตั้งชื่อที่ลงรายละเอียดถึงระดับการทำงานลึกๆละ
{% endhint %}

#### Long Scope

> 🧓 สาเหตุที่ Scope ยาวเราจะต้องตั้งชื่อให้สั้นๆ เพราะ โค้ดที่เรียกใช้ของพวกนี้เขาไม่ต้องการรู้การทำงานจริงๆ ดังนั้นชื่อมันเลยควรจะเป็น High Level ยังไงล่ะ ตามตัวอย่างด้านล่างเบย

**คลาส & เมธอด**

* &#x20;**public class จะนิยมตั้งชื่อสั้นๆ** เพราะคนที่เรียกใช้ ไม่ได้สนใจหรอกว่าจริงๆมันจะต่อกับ printer ประเภทไหน แค่รู้ว่าคลาส Printer ทำงานได้กับเครื่องพิมพ์ได้ทุกตัวก็พอ
* **public method จะนิยมตั้งชื่อสั้นๆ** เพราะคนที่เรียกใช้ก็ไม่สนใจหรอกว่าจริงๆมันต้องทำงานยังไงถึงจะสั่งพิมพ์ได้ แต่รู้ว่าสั่งผ่านเมธอด Print ก็จะพิมพ์เอกสารได้นั่นเอง

```csharp
public class Printer
{
    public void Print() { ... }
}
```

#### Short Scope

> 🧓 สาเหตุที่ Scope สั้นเราจะตั้งชื่อยาวๆ เพราะ โค้ดที่มาเรียกใช้ของพวกนี้ ส่วนใหญ่จะเป็นของที่อยู่ในวงจำกัด และส่วนใหญ่จะอยู่ในระดับ Low Level เรียกใช้กันเองแล้ว ดังนั้นมันเลยต้องตั้งชื่อให้ละเอียดเพื่อความชัดเจน ตามตัวอย่างด้านล่าง

**คลาส**

โค้ดด้านล่าง เราจะเห็นว่า inner class ในบรรทัดที่ 5 มันถูกใช้แค่ภายใน class Printer เท่านั้น ดังนั้นมันจะต้องถูกตั้งชื่อให้อ่านแล้วรู้เรื่องว่ามันมีไว้เพื่อกำหนดค่าเพจสำหรับเอาไว้พิมพ์ ไม่ได้มีไว้ใช้อย่างอื่นนะ

```csharp
public class Printer
{
    // โค้ดต่างๆของของคลาสนี้
    
    private class PageSetupInformation { ... }
}
```

**เมธอด**

โค้ดด้านล่าง เราจะเห็น private method บรรทัดที่ 11\~14 มันถูกใช้แค่ภายใน class Printer เท่านั้น ดังนั้นมันจะถูกตั้งชื่อให้อ่านแล้วรู้ว่ามันแต่ละเมธอดมีหน้าที่อะไรที่ชัดเจน ชื่อมันเลยจะยาวเมื่อเทียบกับ เมธอด Print ในบรรทัดที่ 3 เพราะมันถูกเรียกใช้จากภายนอกได้

```csharp
public class Printer
{
    public void Print()
    {
        connectToPrinter();
        checkInks();
        checkPapers();
        startPrint();
    }
    
    private void connectToPrinter() { ... }
    private void checkInks() { ... }
    private void checkPapers() { ... }
    private void startPrint() { ... }
}
```

### 🔹 คลาสลูก

คลาสลูก **ยิ่งห่างจากชั้นแม่ชื่อยิ่งยาว** เพราะ ลักษณะของคลาสลูกมันจะเจาะจงกับตัวงานมากขึ้น ดังนั้น ยิ่งคลาสลูกยาวเท่าไหร่ มันเลยต้องอธิบายตัวมันเองให้ชัดเจนมากยิ่งขึ้นนั่นเอง ลองดูตัวอย่างของตระกูล Exception ดูก็ได้

![IOException ชัดเจนมาก](/files/-MEh2v1fOHsQ21h4Tw3Y)

## 🥳 ตัวอย่าง

หลังจากที่เห็นหลักในการตั้งชื่อกันไปละ คราวนี้เรามาลองเอามันมาใช้งานจริงกันดูบ้าง ว่ามันจะช่วยเราได้ยังไงกัน

### 😕 ชื่อไม่สื่อ

จากโค้ดด้านล่าง เราจะพบว่าถ้าเราตั้งชื่อที่ไม่สื่อความหมาย มันจะทำให้เราต้องนั่งไล่อ่านโค้ดทุกบรรทัด ซึ่งเมื่อเข้าใจ Algorithm ของมันหมดแล้ว เราก็จะยังไม่รู้อยู่ดีว่าเมธอดนี้มันเอาไว้ทำอะไร

#### 👎 Bad Code

![](/files/-MEh4IC5VVvUTL78a5hY)

#### 👍 Good Code

แต่ถ้าเราตั้งชื่อให้มันสื่อความหมายถึง อย่างน้อยเราก็จะรู้ว่าเมธอดนี้มันจะเอา collection ที่โดนมาร์คไว้กลับมาให้ โดยที่เราไม่ต้องไปไล่อ่านโค้ดทีละบรรทัดเลย หรือต่อให้อ่าน มันก็เข้าใจได้ง่ายขึ้นด้วย

![](/files/-MEh4rgnlH9mGf_PzVUW)

### 😵 ชื่อคล้ายกัน

ลองดูตัวแปร 2 ตัวด้านล่างดิ๊ว่ามันต่างกันตรงไหน? แล้วตอนเราจะเรียกใช้งาน เราจะเลือกถูกตัวแปรป่ะตอนใช้งาน? แล้วถ้ามันมีมากกว่า 2 ตัวอ่ะ?

```csharp
var XYZControllerForEfficientHandlingOfStrings
var XYZControllerForEfficientStorageOfStrings
```

### 💩 ซับซ้อนโดยไม่จำเป็น

#### 👎 Bad Code

```csharp
if(x > 50)
{
    a = 1;
}
else
{
    a = 100;
}
```

#### 👍 Good Code

```csharp
a = x > 50? 1 : 100;
```

> จุดนี้ลุงแกไม่ได้พูดเรื่องนี้หรอก แต่แกบ่นเรื่องตัว lO มันดูแล้วคล้ายกับเลข 1 กับเลข 0 แต่ IDE สมัยใหม่ๆไม่มีปัญหาพวกนี้แล้ว แมวน้ำเลยแถมเรื่องนี้ให้

### 🤬 ชื่อเป็นซีรี่

เจอบ่อยมากในหมู่ เด็กจบใหม่ หรือ คนที่มีประสบการณ์น้อยๆ จะชอบตั้งชื่อตัวแปรประมาณนี้

#### 👎 Bad Code

```csharp
int a1, a2, a3;
int x, y, z;
```

#### 👍 Good Code

ก็ตั้งชื่อให้มันสื่อไปเลย แมวน้ำชอบโดนสวนกลับว่า ก็มันทำงานได้แล้วจะไปเสียเวลาตั้งทำไม? ผมเข้าใจของผมอยู่แล้ว ฮ่วยยยย (เบิดคำซิเว่า) โปรแกรมเมอร์มันทำงานเป็นทีมเฟร้ย ใช่เอ็งเข้าใจเอ็งแก้ได้ แต่คนในทีมไม่เข้าใจมันก็ maintenance ยากขึ้นเฟร้ย !! คิดแล้วโมโหขอบ่นหน่อย

```csharp
int age, score, grade;
```

### 👹 ชื่อน่ารำคาญ

#### Suffixes

ลองดูโค้ดด้านล่างจะเห็นการต่อท้ายชื่อตัวแปรที่น่ารำคาญ เพราะบางทีเราก็จำไม่ได้ว่ามันย่อมาจากอะไร และ คำว่า Data กับ Info ในกรณีนี้มันต่างกันตรงไหน? แล้วตัวเอาเข้าใจริงๆทั้ง 3 ตัวนี้มันต่างกันยังไง?

```csharp
Product product;
ProductData pd;
ProductInfo pi;
```

#### Prefixes

โค้ดด้านล่างเป็นการใส่คำขึ้นต้นที่น่ารำคาญ เช่นพวก A กับ The ดังนั้นอย่าไปใส่

```
Product aProduct;
Product theProduct;
```

### 🥴 ชื่อคลุมเครือ

ลองดูเมธอด 3 ตัวด้านล่างดูดิ๊ แล้วบอกหน่อยว่าเมธอดบรรทัดที่ 2 กับ 3 มันต่างกันตรงไหน? แล้วทำไมเมธอดบรรทัดที่ 3 ไม่เติม s ล่ะในเมื่อมันได้ collection กลับมา?

```csharp
public Account GetActiveAccount() { ... }
public List<Account> GetActiveAccounts() { ... }
public List<Account> GetActiveAccountInfo() { ... }
```

### 🙊 ชื่ออ่านไม่ได้

ถ้าสิ่งที่เราตั้งชื่อมันอ่านออกมาเป็นเสียงไม่ได้ มันจะทำให้เรามีปัญหาเวลาจะบอกคนอื่นว่า สิ่งที่มันมีปัญหาคือจุด !@#$%^&\* นี้นะ 😑

```csharp
string ioactpwd; // ไอ้นี่มันอ่านว่ายังไง? ย่อจากอะไร?
```

## 🙌 ส่งท้ายบท

> 🧓 สรุป 2 บทที่ผ่านมา เราพยายามที่จะทำให้ **โค้ดอธิบายตัวมันเองได้** โดยการ **ไม่เขียนคอมเมนต์** และ ตั้งชื่อให้สื่อความหมาย ซึ่งเราสามารถ **Extract method ออกไปได้เรื่อยๆ** ตราบใดที่มันอ่านแล้วไม่เป็นภาษาคน ซึ่ง
>
> * ไฟล์จะใหญ่ จะหลายบรรทัด ก็ช่าง(หัว)มัน เพราะเราต้องการโค้ดที่แก้ไขได้ง่าย
> * อย่าเขียนโค้ดยาวๆ เพราะไม่มีคนชอบอ่านโค้ดยาวๆ สมองมนุษย์ไม่ได้เก่งเรื่องจำ แต่เก่งเรื่องวิเคราะห์

🐈 ของบางอย่างแมวน้ำก็ไม่ได้ลงรายละเอียดให้นะ เพราะมันถูกเขียนไว้ใน [👶 **Clean Code**](https://www.saladpuk.com/basic/clean-code) ตัวหลักอยู่แล้ว ดังนั้นถ้าสนใจก็ไปกดอ่านเอาเองเด้อ

🌊 บทความนี้ยังไม่จบอย่างที่บอกว่ามันจะแบ่งเป็น 6 ส่วน นี่เป็นแค่ตอนที่ 2 เท่านั้นเอง ดังนั้นตามอ่านกันยาวๆต่อได้จากบทความหลัก หรือไม่อยากพลาดก็ติดตามได้จาก [**Facebook: Mr.Saladpuk**](https://www.facebook.com/mr.saladpuk) ฮั๊ฟ

{% hint style="success" %}
**แนะนำให้อ่าน**\
ในบทความถัดๆไปเราจะเจอหลักในการออกแบบเยอะม๊วก ดังนั้นแมวน้ำแนะนำให้รู้จักหลักในการออกแบบพื้นฐาน 5 ตัว โดยเพื่อนๆสามารถไปอ่านได้จากลิงค์นี้ครัช [👦 **SOLID Design Principles**](https://www.saladpuk.com/basic/solid)
{% endhint %}

## 🎥 วีดีโอลุงบ๊อบ

{% embed url="<https://www.youtube.com/watch?v=2a_ytyt9sf8>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.saladpuk.com/basic/clean-code/uncle-bob-part-3.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
