7 ขั้นตอน การเขียน PHP ให้ Clean ขึ้นด้วย Clean Code 🧹

·
10 พฤศจิกายน 2024
·
Programming, การพัฒนาเว็บไซต์

สวัสดีครับเพื่อนๆ ชาว PHP ทุกคน! ผมเองก็เคยเป็นมือใหม่ที่เขียนโค้ดรกๆ รุงรังเหมือนห้องนอนตอนเช้าวันจันทร์มาก่อน 😅

7 ขั้นตอนการเขียน PHP ให้ Clean ขึ้นด้วย Clean Code

ลองนึกภาพดูนะครับ ถ้าห้องนอนเรารกมากๆ จนหาของไม่เจอ มันก็คงเหมือนกับโค้ดที่ยุ่งเหยิงไม่มีระเบียบ ที่เวลาจะแก้ไขอะไรทีก็ต้องนั่งเกาหัวปวดใจ… แต่ไม่ต้องกังวลไปครับ! วันนี้ผมจะมาแชร์ 7 ขั้นตอนการเขียน PHP ให้ Clean ขึ้นด้วย Clean Code 🧹 ที่จะช่วยให้โค้ดของเพื่อนๆ สะอาด เป็นระเบียบ และดูแลรักษาง่ายขึ้นเยอะ

🌟 ทำไมต้อง Clean Code?

ก่อนจะเข้าเนื้อหา มาดูกันก่อนว่าทำไม Clean Code ถึงสำคัญ:

  1. ประหยัดเวลาในระยะยาว – โค้ดที่สะอาดอ่านง่าย แก้ไขง่าย ทำให้พัฒนาต่อยอดได้เร็วขึ้น
  2. ลดข้อผิดพลาด – โค้ดที่มีระเบียบช่วยลดโอกาสเกิด Bug
  3. ทำงานร่วมกับทีมได้ดีขึ้น – เพื่อนร่วมทีมจะเข้าใจโค้ดของเราได้ง่ายขึ้น
  4. ง่ายต่อการ Maintain – โค้ดที่สะอาดคือการลงทุนเพื่ออนาคต
Clean Code Image

📜 1.Meaningful Names ตั้งชื่อให้สื่อความหมาย

เคยเจอโค้ดแบบนี้มั้ยครับ? 🤔

// ❌Bad Code

function calc($a, $b) {
    return $a + $b;
}

$x = 5;
$y = 10;
$z = calc($x, $y);

โอ้ว… นี่มันอะไรกันเนี่ย? $a, $b, $x, $y ไม่มีใครรู้ว่าตัวแปรพวกนี้เก็บค่าอะไร! มาดูโค้ดที่ดีกว่ากัน:

// ✅Good Code

function calculateTotalPrice(float $basePrice, float $taxAmount): float {
    return $basePrice + $taxAmount;
}

$productPrice = 5.00;
$salesTax = 10.00;
$totalPrice = calculateTotalPrice($productPrice, $salesTax);
Camelcase

🎯 2.Single Responsibility Principle หนึ่งหน้าที่ หนึ่งความรับผิดชอบ

คลาสหรือฟังก์ชันควรมีหน้าที่เดียวที่ชัดเจน เหมือนกับพนักงานในร้านอาหาร พ่อครัวทำอาหาร พนักงานเสิร์ฟเสิร์ฟอาหาร แคชเชียร์คิดเงิน ไม่ใช่ให้คนเดียวทำทุกอย่าง!

// ❌Bad Code

class User {
    public function saveUser($userData) {
        // Validate data
        if (empty($userData['email'])) {
            throw new Exception('Email required');
        }
        
        // Connect to database
        $db = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
        
        // Save to database
        $stmt = $db->prepare("INSERT INTO users ...");
        
        // Send welcome email
        mail($userData['email'], 'Welcome!', 'Thank you for registering...');
    }
}

มาแยก Class ให้ทำหน้าที่เดียวดีกว่า:

// ✅Good Code

class UserValidator {
    public function validate(array $userData): bool {
        return !empty($userData['email']);
    }
}

class UserRepository {
    private $db;
    
    public function save(User $user): void {
        // Database logic only
    }
}

class EmailService {
    public function sendWelcomeEmail(string $email): void {
        // Email logic only
    }
}

class UserRegistration {
    private $validator;
    private $repository;
    private $emailService;
    
    public function register(array $userData): void {
        $this->validator->validate($userData);
        $user = new User($userData);
        $this->repository->save($user);
        $this->emailService->sendWelcomeEmail($userData['email']);
    }
}

🛠 3.Functions/Methods Best Practices เขียนฟังก์ชันให้สวยใสไร้สิว

ผมมีกฎง่ายๆ ในการเขียนฟังก์ชัน: “ทำน้อย ทำดี ทำชัด” 😎

// ❌Bad Code

function handleUser($user, $action, $data = null) {
    if ($action == 'create') {
        if ($data) {
            if (isset($data['name'])) {
                // 20 lines of creation logic
            } else {
                throw new Exception('Name required');
            }
        }
    } elseif ($action == 'update') {
        if ($data) {
            // 20 lines of update logic
        }
    } elseif ($action == 'delete') {
        // 10 lines of deletion logic
    }
}

มาดูโค้ดที่สะอาดกว่ากัน:

// ✅Good Code

class UserModel
{
    public function __construct(private UserRepository $userRepository) {}

    public function createUser(array $userData): User
    {
        $this->validateUserData($userData);
        $user = new User($userData);
        $this->userRepository->save($user);
        return $user;
    }

    public function updateUser(User $user, array $newData): User
    {
        $this->validateUserData($newData);
        $user->update($newData);
        $this->userRepository->save($user);
        return $user;
    }

    public function deleteUser(User $user): void
    {
        $this->userRepository->delete($user);
    }

    private function validateUserData(array $data): void
    {
        if (!isset($data['name'])) {
            throw new ValidationException('Name is required');
        }
    }
}

📝 4.Comments and Documentation – เขียน Comment ให้เป็น

Comment เหมือนเครื่องปรุงอาหารนะครับ ใส่พอดีอร่อย ใส่เยอะเกินพัง! 🌶️

// ❌Bad Comments

// Loop through users
foreach ($users as $user) {
    // Check if user is active
    if ($user->isActive()) {
        // Send email
        $this->mailer->send($user->email);
    }
}

มาดู Comment ที่มีประโยชน์กัน:

// ✅Good Code

/**
 * Sends promotional emails to active users who haven't been
 * contacted in the last 30 days.
 *
 * @param User[] $users Array of user objects
 * @throws MailerException When email service is unavailable
 */
function sendPromotionalEmails(array $users): void {
    $thirtyDaysAgo = new DateTime('-30 days');
    
    foreach ($users as $user) {
        if (!$this->shouldSendPromoEmail($user, $thirtyDaysAgo)) {
            continue;
        }
        
        $this->mailer->sendPromoEmail($user);
    }
}

📏 5.Code Formatting and PSR Standards – จัดโค้ดให้สวย

โค้ดก็เหมือนการแต่งตัวนะครับ ถ้าดูดี ความมั่นใจก็มา! ✨

// ❌Bad Formatting

class userController{
    function create($data){
    $user=new User;
    $user->name=$data['name'];
        $user->email=$data['email'];
            $user->save();
    }
}

มาดูโค้ดที่จัดระเบียบตาม PSR-12:

// ✅Good Code

class UserController
{
    public function create(array $data): User
    {
        $user = new User();
        $user->setName($data['name']);
        $user->setEmail($data['email']);
        $user->save();
        
        return $user;
    }
}

🚨 6.Error Handling จัดการ Error อย่างมืออาชีพ

Error ก็เหมือนปัญหาชีวิตต้องรับมือให้เป็น ไม่ใช่เมินใส่! 😤

// ❌Bad Error Handling

function processPayment($amount) {
    try {
        // process payment
    } catch (Exception $e) {
        // แย่มาก! จับ Exception ทั้งหมดแล้วเงียบเฉย
        error_log($e->getMessage());
    }
}

มาดูการจัดการ Error ที่ดีกว่า:

// ✅Good Code

class PaymentService
{
    public function processPayment(Payment $payment): TransactionResult
    {
        try {
            $this->validatePayment($payment);
            $result = $this->gateway->charge($payment);
            $this->logger->info('Payment processed', [
                'payment_id' => $payment->getId(),
                'amount' => $payment->getAmount()
            ]);
            
            return $result;
        } catch (PaymentValidationException $e) {
            $this->logger->warning('Invalid payment', [
                'payment_id' => $payment->getId(),
                'error' => $e->getMessage()
            ]);
            throw $e;
        } catch (PaymentGatewayException $e) {
            $this->logger->error('Payment gateway error', [
                'payment_id' => $payment->getId(),
                'error' => $e->getMessage()
            ]);
            throw new PaymentFailedException(
                'Payment processing failed',
                $e->getCode(),
                $e
            );
        }
    }
}

🧪 7.Testing Practices เขียนเทสต์อย่างมืออาชีพ

การเขียนเทสต์เหมือนการซ้อมก่อนแสดงจริง ช่วยให้มั่นใจว่าทุกอย่างจะทำงานได้ตามที่ตั้งใจไว้ และช่วยป้องกันบั๊กที่อาจเกิดขึ้นในอนาคต

// ❌Bad Test

class ProductTest extends TestCase
{
    public function testEverything()
    {
        $product = new Product();
        $product->setName('Phone');
        $product->setPrice(1000);
        $product->setStock(5);
        
        $this->assertTrue($product->save());
        $this->assertTrue($product->updateStock(3));
        $this->assertEquals(2, $product->getStock());
    }
}

มาดูโค้ด Testing ที่ดีกัน:

// ✅Good Code

class ProductTest extends TestCase
{
    private Product $product;
    
    protected function setUp(): void
    {
        $this->product = new Product();
    }
    
    public function testProductCanBeSaved(): void
    {
        $this->product->setName('Phone');
        $this->product->setPrice(1000);
        
        $result = $this->product->save();
        
        $this->assertTrue($result);
        $this->assertDatabaseHas('products', [
            'name' => 'Phone',
            'price' => 1000
        ]);
    }
    
    public function testStockCanBeUpdated(): void
    {
        $this->product->setStock(5);
        
        $this->product->updateStock(3);
        
        $this->assertEquals(2, $this->product->getStock());
    }
    
    /**
     * @test
     * @dataProvider invalidStockValues
     */
    public function itShouldThrowExceptionForInvalidStock($invalidValue): void
    {
        $this->expectException(InvalidStockException::class);
        
        $this->product->setStock($invalidValue);
    }
    
    public function invalidStockValues(): array
    {
        return [
            'negative value' => [-1],
            'non numeric' => ['abc'],
            'too large' => [1000001]
        ];
    }
}

🎯 สรุป เริ่มต้นเขียน Clean Code วันนี้!

การเขียน Clean Code อาจดูเหมือนเป็นภาระในตอนแรก แต่เชื่อผมเถอะครับ ว่ามันคุ้มค่าในระยะยาวแน่นอน! เหมือนกับการล้างจานทันทีหลังกินข้าว อาจจะขี้เกียจตอนแรก แต่ดีกว่าปล่อยให้มันกองจนล้างยากในภายหลัง 😅

🔑 Key Takeaways

  1. ตั้งชื่อให้สื่อความหมาย ไม่ต้องให้คนอื่นมานั่งเดาว่าตัวแปรนี้เก็บอะไร
  2. แยกความรับผิดชอบให้ชัดเจน อย่าให้ฟังก์ชันเดียวทำงานหลายอย่าง
  3. เขียนฟังก์ชันให้กระชับ อ่านง่าย
  4. คอมเมนต์เท่าที่จำเป็น แต่ให้มีคุณภาพ
  5. จัดระเบียบโค้ดตามมาตรฐาน PSR
  6. จัดการ errors อย่างเป็นระบบ
  7. เขียน tests ให้ครอบคลุม

📚 แหล่งเรียนรู้เพิ่มเติม

🎯 ลองฝึกปฏิบัติ

  1. เลือกโค้ดเก่าสักส่วนที่คุณเคยเขียนไว้
  2. ลองประเมินตามกฎทั้ง 7 ข้อ
  3. Refactor โค้ดให้สะอาดขึ้น
  4. แชร์ผลลัพธ์กับเพื่อนร่วมทีมและขอ feedback

จำไว้ว่า Clean Code ไม่ได้เกิดขึ้นในวันเดียวมันเป็นทักษะที่ต้องฝึกฝนและพัฒนาไปเรื่อยๆ แต่ยิ่งฝึกเร็ว ยิ่งได้เปรียบ! 💪

แล้วเจอกันใหม่ในบทความหน้านะครับเพื่อนๆ! Happy Coding! 🚀