ในฐานะนักพัฒนาซอฟต์แวร์ระดับ Senior Architect การเข้าใจ Design Patterns ไม่ใช่แค่การเพิ่มความรู้ แต่คือการเพิ่มความสามารถในการออกแบบระบบที่ยั่งยืน (Sustainable System) บทความนี้จะเจาะลึกถึงสามรูปแบบที่ถือเป็นรากฐานสำคัญในการเขียนโค้ดระดับมืออาชีพ ได้แก่ Dependency Injection (DI), Factory Pattern และ Repository Pattern
การใช้รูปแบบเหล่านี้จะช่วยให้โค้ดของคุณมีความสะอาด (Clean Code), สามารถดูแลรักษาได้ง่าย (Maintainable), และที่สำคัญที่สุดคือสามารถทดสอบได้ง่าย (Testable) โดยเฉพาะอย่างยิ่งเมื่อต้องทำงานร่วมกับฐานข้อมูลที่ซับซ้อนอย่าง PostgreSQL
💡 Dependency Injection (DI): การส่งมอบสิ่งที่จำเป็น
แนวคิดหลัก: DI คือหลักการของการควบคุมแบบกลับด้าน (Inversion of Control – IoC) แทนที่ Class หนึ่งจะสร้าง Object ที่ตัวเองต้องใช้ (Hard-coded dependency) เราจะรับ Object นั้นจากภายนอกเข้ามาแทน เปรียบเสมือนการส่งเครื่องมือที่พร้อมใช้งานให้ช่าง แทนที่ให้ช่างไปสร้างเครื่องมือเอง
ปัญหาที่แก้ไข: หาก Service Layer ของคุณสร้าง Connection Object เอง เมื่อมีการเปลี่ยน Connection Pool หรือ Driver คุณจะต้องแก้ไขโค้ด Service Layer นั้นทั้งหมด
การประยุกต์ใช้กับ PostgreSQL: แทนที่จะให้ Service Layer เชื่อมต่อ PostgreSQL โดยตรง เราควร Inject ‘Connection Service’ เข้าไปแทน ซึ่งเป็น Interface ที่กำหนดว่าต้องมีเมธอด `execute_query()` เท่านั้น ทำให้เราสามารถสลับไปใช้ Mock Connection สำหรับการทดสอบ หรือเปลี่ยนไปใช้ Connection Pool อื่นได้โดยไม่กระทบ Service Layer
SQL
-- สมมติว่าเรามี Interface ConnectionService<br>-- และ Service Layer ไม่รู้ว่ามันมาจากไหน<br><br>-- ในโค้ดจริง: DatabaseService(ConnectionService connectionService) { ... }<br><br>-- ตัวอย่างการใช้ Connection Service ที่ถูก Inject เข้ามา:<br>SELECT * FROM users WHERE id = 1;<br>-- โค้ดนี้จะทำงานได้ ไม่ว่า connectionService จะเป็น Postgres หรือ Mock DB
🏭 Factory Pattern: โรงงานสร้าง Object
แนวคิดหลัก: Factory Pattern คือการสร้าง Object ผ่านตัวกลาง (Factory) โดยที่ Client Code ไม่จำเป็นต้องรู้ว่ากำลังสร้าง Object ประเภทใดอย่างเจาะจง เพียงแค่ระบุ ‘เงื่อนไข’ (เช่น ประเภทของฐานข้อมูล) และ Factory จะรับผิดชอบในการสร้าง Object ที่ถูกต้องและเหมาะสมกลับมาให้
การประยุกต์ใช้กับ PostgreSQL: เมื่อระบบของคุณต้องรองรับการเชื่อมต่อฐานข้อมูลหลายประเภท (เช่น PostgreSQL, MySQL, SQLite) Factory จะทำหน้าที่ตัดสินใจว่าจะสร้าง Object Connection Type ใดให้เราตาม Input ที่ได้รับ
SQL
-- ตัวอย่างการใช้ Factory เพื่อเลือก Connection Handler<br>SELECT create_connection_handler('postgres', 'user', 'pass');<br><br>-- Internal logic (จำลอง):<br>CREATE FUNCTION create_connection_handler(db_type TEXT, user TEXT, pass TEXT) RETURNS connection_handler AS $$<br>BEGIN<br> IF db_type = 'postgres' THEN<br> RETURN 'PostgresHandler';<br> ELSIF db_type = 'mysql' THEN<br> RETURN 'MySQLHandler';<br> ELSE<br> RAISE EXCEPTION 'Unsupported DB Type';<br> END IF;<br>END;$$ LANGUAGE plpgsql;
💾 Repository Pattern: การแยกชั้นข้อมูล
แนวคิดหลัก: Repository Pattern คือการสร้างชั้นนามธรรม (Abstraction Layer) ระหว่าง Business Logic กับ Data Access Logic โดยมันจะทำหน้าที่เสมือนเป็นคลังเก็บข้อมูล (In-memory collection) ทำให้ Service Layer สามารถเรียกใช้ข้อมูลได้เหมือนกำลังเรียกใช้ Collection ในหน่วยความจำ โดยไม่จำเป็นต้องรู้ว่าข้อมูลนั้นถูกเก็บใน PostgreSQL, NoSQL หรือไฟล์ใด
การประยุกต์ใช้กับ PostgreSQL: เมื่อคุณต้องการดึงข้อมูลผู้ใช้ (User) Repository จะรับผิดชอบการเขียน SQL ทั้งหมด ไม่ว่าจะเป็น `SELECT * FROM users WHERE id = $1` หรือการจัดการ Transaction ต่างๆ ทำให้ Business Logic สะอาดและไม่ปะปนกับไวยากรณ์ SQL
SQL
-- ตัวอย่าง Repository Method (PostgreSQL Function)<br>-- Repository จะซ่อนรายละเอียดการ Query ทั้งหมดไว้<br>CREATE FUNCTION get_user_by_id(user_id INT) RETURNS TABLE (id INT, name TEXT, email TEXT) AS $$<br>BEGIN<br> RETURN QUERY SELECT id, name, email FROM users WHERE id = $1;<br>END;$$ LANGUAGE plpgsql;<br><br>-- Service Layer เรียกใช้แค่: repository.get_user_by_id(1);
✅ บทสรุป: การทำงานร่วมกันของ Design Patterns
ทั้งสามรูปแบบนี้ไม่ได้ทำงานแยกกัน แต่ทำงานร่วมกันเพื่อสร้างสถาปัตยกรรมที่แข็งแกร่ง:
- Factory ใช้ในการสร้าง Object ที่เหมาะสม (เช่น Connection Object)
- Dependency Injection ใช้ในการส่งมอบ Object ที่สร้างโดย Factory นั้น เข้าไปให้ Service Layer
- Repository ใช้เป็นตัวกลางในการเข้าถึงข้อมูล โดยใช้ Connection Object ที่ถูก Inject เข้ามา
การเข้าใจและประยุกต์ใช้รูปแบบเหล่านี้อย่างถูกต้อง จะช่วยยกระดับโค้ดของคุณให้เป็นระดับ Enterprise-Grade ที่พร้อมต่อการขยายตัวและการบำรุงรักษาในระยะยาว