Interfaces: การกำหนดสัญญา
ในโลกของการเขียนโปรแกรมเชิงวัตถุ (OOP) นอกจากการสืบทอด (Inheritance) แล้ว ยังมีอีกแนวคิดที่สำคัญไม่แพ้กัน นั่นคือ Interface ซึ่งเปรียบเสมือน "สัญญา" ที่ Class ต่างๆ ต้องปฏิบัติตาม หาก Class ใด "รับปาก" ที่จะ Implement Interface นั้นแล้ว จะต้อง Implement ทุก Method ที่ Interface กำหนดไว้
ความหมายของ Interfaces
Interfaces คือโครงสร้างที่กำหนดชุดของ Methods โดยไม่มีการ Implement ใดๆ ทั้งสิ้น กล่าวคือ Interfaces จะบอกเพียงว่า "Class นี้ต้องมี Method อะไรบ้าง" แต่ไม่ได้บอกว่า "Method เหล่านั้นต้องทำงานอย่างไร"
ลองนึกภาพว่าคุณกำลังสร้างระบบแจ้งเตือน (Notification System) คุณอาจต้องการให้ระบบนี้รองรับการแจ้งเตือนผ่านหลายช่องทาง เช่น อีเมล, SMS, หรือ Push Notification แต่ละช่องทางก็อาจมีวิธีการส่งที่แตกต่างกัน แต่สิ่งที่คุณต้องการคือ "ทุกช่องทางต้องมี Method สำหรับส่งข้อความ"
Interface จะช่วยให้คุณกำหนด "สัญญา" นี้ได้ โดยบอกว่า "Class ที่ใช้ส่ง Notification ต้องมี Method ชื่อ `send()`" ส่วนการ Implement ว่า `send()` จะส่งอีเมล, SMS, หรือ Push Notification นั้น เป็นหน้าที่ของแต่ละ Class ที่ Implement Interface
การสร้าง Interfaces
ใน PHP เราสร้าง Interface โดยใช้ Keyword `interface` ตามด้วยชื่อ Interface และภายใน Block `{}` จะเป็นการกำหนด Methods ที่ Interface นั้นต้องการ
<?php
interface Logger {
public function log(string $message);
}
?>
จากตัวอย่างด้านบน เราได้สร้าง Interface ชื่อ `Logger` ซึ่งกำหนดว่า Class ที่ Implement `Logger` จะต้องมี Method ชื่อ `log()` ที่รับ Parameter เป็น String และไม่มีการ Return ค่า
ข้อสังเกต:
- Methods ใน Interface จะต้องเป็น `public` เสมอ เพราะจุดประสงค์ของ Interface คือการกำหนดสิ่งที่ Class อื่นๆ สามารถเรียกใช้ได้
- Methods ใน Interface ไม่มี Body (ไม่มี Code ภายใน `{}`) เพราะ Interface เป็นเพียงการกำหนดโครงสร้าง ไม่ได้กำหนดการทำงาน
การ Implement Interfaces
เมื่อเรามี Interface แล้ว เราสามารถให้ Class ต่างๆ "Implement" Interface นั้นได้ โดยใช้ Keyword `implements` ตามด้วยชื่อ Interface
<?php
interface Logger {
public function log(string $message);
}
class FileLogger implements Logger {
private $filePath;
public function __construct(string $filePath) {
$this->filePath = $filePath;
}
public function log(string $message) {
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
}
?>
จากตัวอย่างด้านบน เราได้สร้าง Class ชื่อ `FileLogger` ซึ่ง Implement Interface `Logger` นั่นหมายความว่า `FileLogger` จะต้องมี Method ชื่อ `log()` ที่รับ Parameter เป็น String และไม่มีการ Return ค่า
ภายใน Class `FileLogger` เราได้ Implement Method `log()` โดยการเขียนข้อความลงในไฟล์ที่กำหนดไว้ใน Constructor
ข้อสำคัญ: หาก Class ใด Implement Interface แล้ว แต่ไม่ได้ Implement Methods ทั้งหมดที่ Interface กำหนดไว้ PHP จะ Throw Error ทันที
ตัวอย่าง:
<?php
interface Logger {
public function log(string $message);
public function error(string $message);
}
class FileLogger implements Logger {
private $filePath;
public function __construct(string $filePath) {
$this->filePath = $filePath;
}
public function log(string $message) {
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
}
// Warning: Class FileLogger contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Logger::error)
?>
ในตัวอย่างนี้ Interface `Logger` กำหนดให้มี Method `log()` และ `error()` แต่ Class `FileLogger` Implement เพียง `log()` เท่านั้น ทำให้ PHP Throw Warning ว่า Class `FileLogger` ยังมี Abstract Method ที่ต้อง Implement
เราสามารถแก้ไขได้โดยการ Implement Method `error()` ใน Class `FileLogger` ดังนี้
<?php
interface Logger {
public function log(string $message);
public function error(string $message);
}
class FileLogger implements Logger {
private $filePath;
public function __construct(string $filePath) {
$this->filePath = $filePath;
}
public function log(string $message) {
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
public function error(string $message) {
error_log($message, 3, $this->filePath); // Log error using error_log
}
}
?>
นอกจากนี้ Class หนึ่งสามารถ Implement หลาย Interfaces ได้ โดยใช้ Comma (`,`) คั่นระหว่างชื่อ Interface
<?php
interface Loggable {
public function log(string $message);
}
interface Auditable {
public function audit(string $action);
}
class User implements Loggable, Auditable {
private $username;
public function __construct(string $username) {
$this->username = $username;
}
public function log(string $message) {
// Implement logging logic here
echo "Logging: " . $message . PHP_EOL;
}
public function audit(string $action) {
// Implement auditing logic here
echo "Auditing: " . $action . " for user " . $this->username . PHP_EOL;
}
}
$user = new User("john_doe");
$user->log("User logged in");
$user->audit("Created account");
?>
จากตัวอย่างนี้ Class `User` Implement ทั้ง Interface `Loggable` และ `Auditable` ทำให้ Class `User` ต้อง Implement Method `log()` จาก `Loggable` และ Method `audit()` จาก `Auditable`
สรุป: Interfaces เป็นเครื่องมือที่สำคัญในการออกแบบ OOP ที่ช่วยให้เรากำหนด "สัญญา" ที่ Class ต่างๆ ต้องปฏิบัติตาม ทำให้ Code ของเรามีความยืดหยุ่น, บำรุงรักษาง่าย, และสามารถขยายความสามารถได้ง่ายขึ้น ในบทถัดไป เราจะมาดูเรื่อง Polymorphism ซึ่งเป็นอีกหนึ่งแนวคิดที่สำคัญที่ทำงานร่วมกับ Interface ได้อย่างมีประสิทธิภาพ