หมวดหมู่: Best Practices

PHP: การหลีกเลี่ยง Global Variables และ Global FunctionsPHP: การหลีกเลี่ยง Global Variables และ Global Functions

ในฐานะ Senior Architect ด้านระบบ Back-end การเขียนโค้ดที่ ‘ทำงานได้’ (Functionality) เป็นเพียงจุดเริ่มต้นเท่านั้น หัวใจสำคัญของการพัฒนาระดับองค์กรคือการสร้างโค้ดที่สามารถบำรุงรักษาได้ง่าย (Maintainability), ทดสอบได้ง่าย (Testability), และขยายขนาดได้ (Scalability) การพึ่งพา Global Variables หรือ Global Functions คือจุดอ่อนทางสถาปัตยกรรมที่ร้ายแรงที่สุด

Global Scope คือพื้นที่ที่ตัวแปรหรือฟังก์ชันสามารถเข้าถึงได้จากทุกส่วนของโปรแกรมโดยไม่มีการกำหนดขอบเขตที่ชัดเจน เมื่อระบบของเราเริ่มพึ่งพา Global State มากเกินไป โค้ดจะสูญเสียความเป็นโมดูลาร์ (Modular) และกลายเป็นสิ่งที่ยากต่อการติดตามการเปลี่ยนแปลง (Side Effects) เพราะการเปลี่ยนแปลงค่าในตัวแปร ณ จุด A อาจส่งผลกระทบต่อตรรกะการทำงานที่จุด Z โดยที่เราไม่รู้ล่วงหน้า

หลักการแก้ไขปัญหาที่ถูกต้องตามหลักการออกแบบซอฟต์แวร์ (Design Patterns) คือการใช้แนวคิด Encapsulation และ Dependency Injection (DI) เราต้องส่งผ่านข้อมูลหรือบริการที่จำเป็น (Dependencies) เข้าไปในฟังก์ชันหรือคลาสอย่างชัดเจน แทนที่จะปล่อยให้มันลอยอยู่ใน Global Scope การทำเช่นนี้ทำให้โค้ดของเราเป็นแบบ Predictable และสามารถ Unit Test ได้อย่างสมบูรณ์

การประยุกต์ใช้หลักการ DI ด้วย PostgreSQL

แม้ว่าปัญหา Global Variables จะเกิดขึ้นในระดับภาษา PHP แต่การแก้ปัญหาต้องอาศัยหลักการสถาปัตยกรรมที่เข้มงวด ซึ่งสามารถนำไปประยุกต์ใช้กับการจัดการข้อมูลใน PostgreSQL ได้ เราควรออกแบบฟังก์ชันหรือ Stored Procedure ให้รับพารามิเตอร์ที่จำเป็นทั้งหมด แทนที่จะพึ่งพาตัวแปรสภาพแวดล้อม (Environment Variables) ที่อาจถูกกำหนดไว้ภายนอก

ตัวอย่างปัญหา (Global Dependency): การเขียนฟังก์ชันที่สมมติว่ามีตัวแปรเชื่อมต่อฐานข้อมูล (Connection Object) อยู่ใน Global Scope

SQL

-- BAD PRACTICE: Global Dependency (สมมติว่ามีตัวแปร 'global_conn' อยู่) 
SELECT * FROM users WHERE status = 'active' AND user_id = global_conn.user_id;

ตัวอย่างการแก้ไข (Dependency Injection): เราต้องส่ง Connection Object หรือค่าที่จำเป็นทั้งหมดเป็นพารามิเตอร์เข้าสู่ฟังก์ชันอย่างชัดเจน ทำให้ฟังก์ชันนั้นเป็นอิสระ (Self-contained) และสามารถนำไปใช้ซ้ำได้

SQL

-- GOOD PRACTICE: Dependency Injection (ส่ง connection และ ID เข้ามาเป็นพารามิเตอร์) 
CREATE OR REPLACE FUNCTION get_user_data(p_connection_id INT, p_status TEXT) 
RETURNS TABLE (user_data JSON) 
AS $$ 
BEGIN 
    RETURN QUERY SELECT to_json(u) FROM users u WHERE u.user_id = p_connection_id AND u.status = p_status; 
END; 
$$ LANGUAGE plpgsql; 

-- การเรียกใช้งานที่ชัดเจน: ต้องระบุค่าที่จำเป็นทั้งหมด 
SELECT * FROM get_user_data(101, 'active');

บทสรุป: การสร้างโค้ดที่แข็งแกร่ง

การหลีกเลี่ยง Global State ไม่ใช่แค่เรื่องของไวยากรณ์ (Syntax) แต่เป็นเรื่องของสถาปัตยกรรม (Architecture) ในฐานะ Senior Developer เราต้องบังคับใช้หลักการ Dependency Injection เสมอ ไม่ว่าจะเป็นการส่ง Connection Object, Configuration Values, หรือแม้แต่ค่า ID เข้าไปในฟังก์ชันอย่างชัดเจน การทำเช่นนี้จะทำให้โค้ด PHP ของเรามีความเป็นโมดูลาร์สูง (Highly Modular), สามารถทดสอบแยกส่วน (Unit Test) ได้ง่าย, และพร้อมสำหรับการขยายตัวในอนาคตอย่างแท้จริง