Clean Code 如何寫出更乾淨的程式碼

當每個你看到的程式,執行結果都與你想得差不多,你會察覺到你正工作在 Clean Code 上

你是否經常遇到一些惱人或在維護時連自己都看不懂的程式碼呢?程式碼不僅是寫給機器看,更是為寫給人看。

程式碼的可讀性很重要,是日後維護的關鍵,以下介紹幾個簡單的規則,幫助寫出更乾淨的程式碼:

Any fool can write code that a computer can understand. Good programmers write code that humans can understand - Martin Fowler

1. 有意義的命名

不使用 $x $y 之類的變數名稱,沒人知道是什麼意思

// Bad
foreach ($x as $y) {...}

// Good
foreach ($people as $person) {...}

讓數字變得有意義

// Bad
$users = $userRepository->fetchByType(1);

// Good
$users = $userRepository->fetchByType(User::TYPE_ADMIN);

2. 不使用縮寫

寫程式常常會用縮寫代表的意義,可能為了讓變數名稱看起來更簡短,或者減少打字的時間!? 但這都是不需要的,縮寫會導致程式碼可讀性更差,讓意義變得更模糊,就把完整名稱打出來吧!

// Bad
class Trnsltr {...}

// Good
class Translator {...}

像一些很常見的縮寫,一看就知道代表什麼意思,就不用把完整名稱打出來了

// Bad
echo $userIdentification;

// Good
echo $userId;

3. 不必要的變數宣告

有時候加上不必要的變數反而會造成混亂,更加難以閱讀

// Bad
class User
{
    ...

    public function isPremium(): bool
    {
        if ($this->type === static::TYPE_PREMIUM) {
            $result = true;
        } else {
            $result = false;
        }

        return $result;
    }
}

// Good
class User
{
    ...

    public function isPremium(): bool
    {
        if ($this->type === static::TYPE_PREMIUM) {
            return true;
        } else {
            return false;
        }
    }
}

// Best
class User
{
    ...

    public function isPremium(): bool
    {
        return $this->type === static::TYPE_PREMIUM;
    }
}

4. 盡量讓名稱保持在兩個字詞以下

過長的方法名稱描述也代表這個方法做了過多的事情,讓程式碼保持簡單,做的事情只有一件事

// Bad
class Post
{
    public funciton saveAndUploadPost() {...}
}

// Good
class Post
{
    public funciton savePost() {...}
    public funciton uploadPost() {...}
}

// Best
class Post
{
    public funciton save() {...}
    public funciton upload() {...}
}

名稱太長有時候也可以考慮抽成 class

// Bad
function isShopOpen($day) {...}

// Good
class Shop
{
    public function isOpen($day) {...}
}

有相同類型的對象描述,可以抽成 Entity

// Bad
$userName;
$userPhone;
$userEmail;

// Good
class User
{
    public $name;
    public $phone;
    public $email;
}

但還是要看情況,有時候為了完整描述做的一件事情這樣做是更好的

$posts = $postRepository->fetchAllByIdAndDate($id, $date);

5. 盡量保持一層縮排

過多層數的縮排會影響閱讀,也會產生所謂的波動拳程式碼,利用 Early Return,或者 PHP Array Functions 或者 Collections 去處理,增加可讀性

Early Return,可以利用 Type Hint 除去檢查類型的程式碼,再利用 array 把相關的邏輯封裝,並且不用 else

// Bad
class Shop
{
    public function isOpen($day): bool
    {
        if ($day) {
            if (is_string($day)) {
                $day = strtolower($day);
                if ($day === 'friday') {
                    return true;
                } elseif ($day === 'saturday') {
                    return true;
                } elseif ($day === 'sunday') {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else {
            throw new Exception('$day is empty');
        }
    }
}

// Good
class Shop
{
    public function isOpen(string $day): bool
    {
        if (empty($day)) {
            throw new Exception('$day is empty');
        }

        $day = strtolower($day);
        if ($day === 'friday') {
            return true;
        } elseif ($day === 'saturday') {
            return true;
        } elseif ($day === 'sunday') {
            return true;
        } else {
            return false;
        }
    }
}

// Best
class Shop
{
    public function isOpen(string $day): bool
    {
        if (empty($day)) {
            throw new Exception('$day is empty');
        }

        $openDays = ['friday', 'saturday', 'sunday'];

        return in_array(strtolower($day), $openDays);
    }
}

多利用 PHP Array Functions 來處理問題,也可以增加可讀性

// Bad
class Posts
{
    ...

    public function filterBy($type): array
    {
        $filtered = [];
        foreach ($this->posts as $post) {
            if ($post->isPublished) {
                if ($post->type === $type) {
                    $filtered[] = $post;
                }
            }
        }

        return $filtered;
    }
}

// Good
class Posts
{
    ...

    public function filterBy($type): array
    {
        return array_filter($this->posts, function (Post $post) use ($type) {
            return $post->isPublished && $post->type === $type;
        });
    }
}

總結

以上簡單整理了一些常常出現並且可以改進的地方,程式碼是死的,人是活的,規則沒有一定,多與其他人討論,多接觸不一樣的想法,程式碼可以更靈活、乾淨

如果有什麼問題歡迎指出或討論

References