# Clean Code

## 😢 ปัญหา

เคยอ่านโค้ดแล้วกร่นด่าในใจกันไหม "นี่มันโค้ดบ้าอะไร?", "ทำไมเขียนแบบนี้ฟร๊า?", "จะเริ่มจากตรงไหนดี?" บลาๆ ซึ่งถ้าเกิดเหตุการณ์แบบนี้ขึ้นนั่นแสดงว่า **เราจะทำงานได้ช้าลง** เพราะต้องเสียเวลานั่งทำความเข้าใจเจ้าโค้ดพวกนั้น และเลวร้ายที่สุดคือการที่พบว่า **ลบโค้ดพวกนั้นทิ้งแล้วเขียนใหม่ดีกว่า** นั่นแสดงว่าทีมต้องเสียเวลาไปทำงานเดิมซ้ำสอง

โค้ด !@#$%^&\* พวกนั้นเราเรียกมันว่า **Bad Code** ซึ่งเราควรจะเปลี่ยนมาเขียน **Good Code** กัน

## ❓ ทำไมต้องแก้ Bad Code ?

ถ้าในโปรเจคมี Bad Code อยู่เยอะๆ คนอื่นๆในทีมก็จะเห็น Bad Code เหล่านั้น เมื่อเห็นเรื่อยๆเห็นบ่อยๆ มันก็มีโอกาสสูงที่ทีมจะคิดว่านั่นคือเรื่องธรรมดา เลยทำให้ทีมบ่มนิสัยผลิตแต่โค้ดที่กากๆออกมา ทำให้**โปรเจคนี้และโปรเจคต่อๆไปเราก็จะเจอแต่โค้ดที่ทำให้ทีมทำงานได้ช้าลงตลอดไป**

สมมุติว่าเราต้องเพิ่มความสามารถใหม่ให้โค้ดหรือต้องไปแก้ bug กับงานที่มี Bad Code เยอะๆ จะได้อารมณ์ประมาณภาพด้านล่างนี้

> **เส้นสีเขียว** - คือเราต้องเดินไปไหนบ้างถึงจะไปเจอจุดที่ต้องไปเขียนโค้ดจริงๆ\
> **โซนสีส้ม** - คือจุดที่ต้องเข้าไปเขียนโค้ด

![ภาพจาก ronjeffries.com](https://479516123-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lm0_idNbY6k1lwp6hm4%2F-M1lfqlFTvI3gmheTI_q%2F-LrKUKZC4t0wS7zDA35L%2Fimage.png?generation=1583529169541827\&alt=media)

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

คราวนี้มาดูงานที่มีแต่ **Good Code** ที่ทุกอย่างเป็นระเบียบเรียบร้อยกันบ้าง

![ภาพจาก ronjeffries.com](https://479516123-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lm0_idNbY6k1lwp6hm4%2F-M1lfqlFTvI3gmheTI_q%2F-LqpyJEvEObIhwoPuvG9%2Fimage.png?generation=1583529164814561\&alt=media)

จะเห็นว่า **Good Code** ทำให้เราเข้าถึงจุดของงานได้เร็ว และ ลดโอกาสเกิดข้อผิดพลาดลง

## 🤔 ดูยังไงว่าโค้ดเราดีหรือยัง ?

การดูว่าโค้ดเราดีหรือเปล่าให้ดูจาก **คำด่าต่อนาที** ตอนทำ **Code Review**

![](https://479516123-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lm0_idNbY6k1lwp6hm4%2F-M1lfqlFTvI3gmheTI_q%2F-LsZgRcCygkf3qGB6E-_%2Fimage.png?generation=1583529163714539\&alt=media)

{% hint style="info" %}
**Code Review**\
คือชั่วโมงที่เราเอาโค้ดทั้งของเราและของคนอื่นๆมากางออก แล้วไล่ดูว่ามีโค้ดจุดไหนที่ต้องปรับแก้ ตรงไหนควรสร้าง code standard บลาๆ ซึ่งวัตถุประสงค์ของมันคือ**ช่วยให้ทีมได้เรียนรู้และไม่ทำโค้ดกากๆออกมา**
{% endhint %}

## 😄 วิธีแก้ปัญหา

> ก็อย่าเขียน **Bad Code** แต่แรกดิ !! (ไม่บอกก็รู้เฟร้ย 🤣)

การเขียน **Good Code** นั้นเป็นเรื่องของความเอาใจใส่ในคุณภาพของโค้ด ซึ่งการเขียนโค้ดมันก็เหมือนกับคุณเขียนหนังสือซักเล่มนั่นแหละ ถ้าคุณเขียนแล้วคนอื่นเอาไปอ่านแล้วไม่เข้าใจ มันก็หมายถึงคุณเขียนได้ไม่ดี #โค้ดก็เช่นกัน

งั้นเดี๋ยวเราไปดูมาตรฐานสากลของ **Good Code** แล้วเทียบกับ **Bad Code** กันเรย

### 🔥 การเขียนเงื่อนไข

* **อย่าเขียนเงื่อนไขที่ให้คนอ่านต้องแปลความหมาย** เพราะมันจะเสียเวลาทำความเข้าใจ และอาจะเข้าใจผิดได้

> ลองดูโค้ดด้านล่างแล้วทำความเข้าใจดูครับว่ามันจะสื่อว่าอะไร ?\
> แล้วลองเปรียบเทียบ Bad Code กับ Good Code ดู

**Bad Code**

```csharp
if(Math.Pow(banaCa.Extana + 3 / 1.2, 2) >= Policy && (FullName.StartWith("LUNA") || LastName == "Covan" ))
{
   // เข้าผับได้
}
else
{
   // เข้าผับไม่ได้
}
```

**Good Code**

```csharp
var canEnterThePub = Math.Pow(banaCa.Extana + 3 / 1.2, 2) >= Policy && (FullName.StartWith("LUNA") || LastName == "Covan" );
if(canEnterThePub)
{
   // เข้าผับได้
}
else
{
   // เข้าผับไม่ได้
}
```

> ใจความของตัวอย่างคือ เราเข้าใจเงื่อนไขได้เร็วขนาดไหน\
> \* **Bad Code** เราต้องไล่ดูทีละเงื่อนไขทีละตัวใน if เพื่อจะได้รู้ว่ามันกำลังตรวจเรื่องอะไร\
> \* **Good Code** เราไม่ต้องไล่ดูเงื่อนไขเลย เพราะอ่านใน if ปุ๊ปเราก็เข้าใจได้ทันที

### 🔥 คอมเมนต์

* **อย่าเขียนคอมเมนต์!!** ถ้าเขียนคอมเมนต์แสดงว่าโค้ดคุณไม่ได้เข้าใจง่ายพอ คุณเลยต้องเขียนคอมเมนต์ทิ้งไว้ให้ชนรุ่นหลังได้เข้าใจ วิธีแก้คือสร้าง method ที่ทำให้เข้าใจได้ง่ายแทน
* **อย่าคอมเมนต์โค้ดที่เผื่อจะได้ใช้** เพราะคนอื่นไม่รู้สาเหตุหรอกว่าคุณคอมมเมนต์มันไว้ทำไม และเขาจะไม่กล้าลบมันแล้วมันจะกลายเป็นขยะค้างไว้แบบนั้น **ลบมันทิ้งซะ เพราะยังไงก็อยู่ใน commit อยู่แล้ว**

**Bad Code**

```csharp
/// <summary>
/// เปลี่ยนแปลงที่อยู่ผู้ใช้ใหม่
/// </summary>
/// <param name="email">อีเมล์ผู้ใช้</param>
/// <param name="address">ข้อมูลที่อยู่ใหม่</param>
public void UpsertAddress(string email, Address address)
{
    // ตรวจสอบหาข้อผิดพลาด
    if(address == null)
    {
        throw new Exception("Address เป็น null");
    }

    // ค้นหาผู้ใช้และตรวจว่ามีผู้ใช้ในระบบหรือไม่
    var user = Database.User.Get(it => it.Email == email);
    if(user == null)
    {
        throw new Exception("หาผู้ใช้ไม่พบ");
    }
    else
    {
        address.UserId = user.Id;
    }

    // ดึงข้อมูลที่อยู่ผู้ใช้มาทำการแก้ไข
    if(Database.Address.Get(it => it.UserId == user.Id) == null)
    {
        Database.Address.Insert(address);
    }
    else
    {
        Database.Address.Update(address);
    }

    // เก็บเอาไว้ก่อนเผื่อได้ใช้ในอนาคต
    //if(user.Setting.SendNotificationWhenAddressChanged)
    //{
    //    Email.Send(email, address, "คุณได้ทำการเปลี่ยนที่อยู่ใหม่");
    //}
}
```

**Good Code**

```csharp
public void UpsertAddress(string email, Address address)
{
    var user = getUserByEmail(email);
    updateAddress(user, address);
}

private UserProfile getUserByEmail(string email) ...
private void updateAddress(UserProfile user, Address address) ...
```

> ลดการเสียเวลาในการอ่านโค้ด โดยการสร้าง method เพื่อให้คนอ่านเห็นเป็นภาษามนุษย์มากขึ้น ไม่ใช่ต้องไปไล่อ่านโค้ดเพื่อทำความเข้าใจว่าเจ้า 10 บรรทัดนี้คืออะไร
>
> จากตัวอย่างจะสังเกตุได้ว่า\
> ใน Good Code บรรทัด 3 มันแทนที่ 14-19 ใน Bad Code ได้หมดเลย (และอ่านเป็นภาษามนุษย์ด้วย)\
> ใน Good Code บรรทัด 4 มันแทนที่ 25-33 ใน Bad Code ได้หมดเลย (และอ่านเป็นภาษามนุษย์ด้วย)\
> ส่วน 8-12 และ 20-23 ใน Bad Code มันถูกย้ายไปจัดการใน updateAddress() เรียบร้อยแล้ว\
> และ 35-39 ถ้าไม่ได้ใช้ก็ลบทิ้งซะอย่างเก็บมันไว้ คนอื่นจะไม่กล้ายุ่งกับโค้ดคุณ

### 🔥 การตั้งชื่อทั่วไป

* เวลาจะตั้งชื่ออะไรก็แล้ว **ต้องตั้งให้มันสื่อความหมาย** อ่านแล้วเข้าใจว่ามันมีไว้เพื่ออะไร
* ชื่อต้อง **อ่านออกเสียงได้** ถ้ามันออกเสียงไม่ได้เวลาจะบอกคนอื่นว่ามันผิดที่ไหน คุณจะบอกเขาว่ายังไง?
* **หลีกเลี่ยงคำย่อ** เพราะทุกคนย่อคำไม่เหมือนกัน เช่น Student คุณจะย่อว่าอะไร (std? โรคติดต่อทางเพศงั้นเหรอ?)
* ชื่อควร**ตั้งชื่อตามพฤติกรรม** เช่น ชื่อ**คลาสควรเป็นคำนาม**, ชื่อ **method ควรขึ้นด้วยคำกิริยา**
* **อย่าตั้งชื่อยาว** เพราะมันจะเสียเวลาทำความเข้าใจ
* **อย่าตั้งชื่อเป็นนิเสธ** เพราะคนอ่านจะ งง (แค่คำว่านิเสธก็ งง แล้ว)
* **อย่างตั้งชื่อกำกวม** เพราะมันจะสับสนว่าตัวไหนเป็นตัวไหนกันแน่

**Bad Code**

```csharp
double m; // ตัวแปรตัวนี้ใช้ทำอะไร ?
string actpwd; // ไอ้นี่มันอ่านว่ายังไง? ย่อจากอะไร?
string generatedSaltValueForEncryptTheAccountPasswordForThisUserOnly; // เสียเวลาอ่านจุง
string data; // กำกวมเกิน สรุปมันคือ data เรื่องอะไร?
```

**Good Code**

```csharp
double money; // อ่านแล้วรู้เลยว่าใช้เก็บเรื่องเงิน
string accountPassword; // อ่านแล้วรู้เลยว่าเก็บรหัสผ่านของผู้ใช้
string salt4Password; // อ่านแล้วรู้เลยว่าเก็บรหัสผ่าน
string userData; // อ่านแล้วรู้เลยว่าเก็บข้อมูลผู้ใช้
```

{% hint style="info" %}
**เกร็ดความรู้**\
คลีนโค้ดบางสำนักไม่แนะนำให้ตั้งชื่อแบบใส่ตัวเลขแทนคำอ่านนะ เช่น Good Code บรรทัดที่ 3 อาจจะเขียนเต็มๆไปเลยก็ได้ saltForPassword
{% endhint %}

### 🔥 การตั้งชื่อ Methods & Functions

> กฎในการตั้งชื่อทั่วไปทั้งหมดก็ใช้กับการตั้งชื่อ Methods & Functions ด้วยนะจ๊ะ

* **ขึ้นต้นด้วยคำกิริยา** เพราะคนเรียกใช้อ่านแล้วรู้เลยว่าใช้ทำอะไร (คนไทยอาจะไม่มีผลเท่าไหร่แต่ฝรั่งนี่มีสูงเลย)
* **อย่ารับ arguments เยอะ** เพราะคนเรียกใช้ method อาจ งง ใส่ผิด และเวลาเพิ่ม/ลดละก็อวกแตก
* ตั้งชื่อให้**สื่อว่างานของมันคืออะไร** ไม่งั้นคนเรียกใช้ method จะมีคำถาม

**Bad Code**

```csharp
AccountNameChanger(string name) // ต้องอ่านทั้งหมดถึงจะเข้าใจ
CreateAccount(string uName, string pwd, ...) // Arguments จะเยอะไปไหน

ChangeAccountPasswordAndDeleteIfNotValid() // เอ็งจะเปลี่ยนรหัสผ่านหรือจะลบผู้ใช้กันแน่ ?
```

**Good Code**

```csharp
ChangeAccountName(string name) // อ่านแล้วเข้าใจเลยเพราะเห็นคำกิริยาก่อนเพื่อน
CreateAccount(AccountInfo account) // คนเรียกไม่สับสน

ChangeAccountPassword() // อ่านแล้วรู้เลยว่าแค่เปลี่ยนรหัสผ่าน
DeleteAccountIfNoBirthDate() // อ่านแล้วรู้เลยว่าจะลบผู้ใช้กรณีไหน
```

### 🔥 Layout

* ควรจัด**วาง Layout ให้ตรงกับมาตรฐานของภาษาที่ใช้** เพราะโค้ดของทั้งทีมจะได้เหมือนกัน และเวลาทำงานกับ Git ก็จะไม่เกิด conflict ด้วย

**Bad Code**

```csharp
if( conditionA ){
   // Do something-1
}
else if(conditionB)
{
   // Do something-2
}
else
   // Do something-3

// สรุปเอ็งจะใช้ layout ของ { } และการเว้นวรรคแบบไหนกันแน่?
```

**Good Code**

```csharp
if(conditionA)
{
   // Do something-1
}
else if(conditionB)
{
   // Do something-2
}
else
{
   // Do something-3
}
```

## 🎯 บทสรุป

เรื่อง **Clean Code** มันเป็นเรื่องความเอาใจใส่ในผลงานของตัวเอง ซึ่งผลลัพท์ที่ได้มันจะทำให้เราไม่เสียเวลาหลงทางในเขาวงกต และมันไม่ได้มีแค่เท่านี้หรอก อันนี้เป็นเพียงน้ำจิ้มส่วนนึงเท่านั้น และแต่ละภาษาก็มีหลักในการทำ clean code ในรูปแบบของเขาอยู่เช่นกัน เลยขอดึงเฉพาะตัวที่เป็นกลางๆมาให้ดูก่อน ไว้มีโอกาสจะเอามาเพิ่มให้อ่านนะจุ๊

{% hint style="danger" %}
**ทำทันที**\
เรื่องการ Clean Code ห้ามปล่อยทิ้งไว้ เพราะ **คุณไม่กลับมาแก้หรอก!!** ดังนั้นจงจำไว้ **"ทำ-ทัน-ที"**
{% endhint %}

{% hint style="info" %}
**เกร็ดความรู้**\
มาตรฐานทั้งหมดที่ว่านี้ ในแต่ละภาษามันจะมี Coding Standard ของเขาเองอยู่นะ ลองไปหาอ่านได้ เช่น มาตรฐานของฝั่ง .NET สามารถอ่านได้จากลิงค์นี้ครับ [Microsoft document](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/)
{% endhint %}
