OOP Bài 2: Sự khác biệt cốt lõi giữa Lập trình thủ tục (POP) và OOP.
1. Triết lý thiết kế (Cốt lõi của sự khác biệt)
- Lập trình thủ tục (POP - Procedural Oriented Programming): Tách rời Dữ liệu và Hành động. Hệ thống được coi là một tập hợp các Hàm (Functions). Dữ liệu chạy quanh hệ thống, từ hàm này sang hàm khác giống như nguyên liệu chạy qua một dây chuyền nhà máy. Bạn tư duy theo chuỗi hành động: Bước 1 làm gì -> Bước 2 làm gì.
- Lập trình hướng đối tượng (OOP - Object-Oriented Programming): Gắn kết Dữ liệu và Hành động. Hệ thống được coi là một tập hợp các Thực thể (Objects) giao tiếp với nhau. Dữ liệu và các hàm thao tác trên dữ liệu đó được đóng gói lại thành một khối. Bạn tư duy theo hướng phân bổ trách nhiệm: Thực thể nào chịu trách nhiệm quản lý thông tin này?
2. Bảng so sánh tổng quan
| Tiêu chí | Lập trình thủ tục (POP) | Lập trình hướng đối tượng (OOP) |
|---|---|---|
| Đơn vị cấu thành | Hàm (Functions / Procedures) | Đối tượng (Objects) |
| Cách tiếp cận | Top-Down (Từ trên xuống dưới) | Bottom-Up (Từ dưới lên trên) |
| Trạng thái dữ liệu | Trôi nổi tự do (Global / Passed by ref) | Đóng gói an toàn (Encapsulated) |
| Bảo mật nội bộ | Dữ liệu dễ bị thay đổi ngoài ý muốn | Chỉ Object mới được phép đổi dữ liệu của chính nó |
| Khả năng mở rộng | Rất khó, sửa một hàm có thể hỏng toàn cục | Rất tốt, dễ dàng thêm Class mới mà không phá vỡ core |
| Ví dụ ngôn ngữ | C, Pascal, Assembly, PHP thuần | C++, Java, C#, PHP (OOP), Go (Struct & Interfaces) |
3. Mã nguồn thực chiến: POP vs OOP
Để thấy rõ sự khác biệt, hãy lấy ví dụ về việc xử lý trừ tiền vé tàu điện (AFC system) khi hành khách đi qua cửa xoay (Turnstile).
Cách viết kiểu POP (Lập trình thủ tục)
Trong POP, dữ liệu chiếc vé chỉ là một mảng (hoặc struct) vô tri. Bất kỳ hàm nào cũng có quyền lôi nó ra và sửa đổi.
// 1. Dữ liệu nằm trơ trọi
$ticket = [
'id' => 'TICK_999',
'balance' => 50000,
'status' => 'active'
];
// 2. Hàm xử lý nằm ở một nơi khác
function deductFare(&$ticketData, $amount) {
if ($ticketData['status'] !== 'active') {
throw new Exception("Vé không hợp lệ");
}
if ($ticketData['balance'] >= $amount) {
$ticketData['balance'] -= $amount; // Sửa đổi dữ liệu trực tiếp
return true;
}
return false;
}
// 3. Thực thi
deductFare($ticket, 15000);
Vấn đề ở đây là gì? Dữ liệu
$ticketkhông có khả năng tự bảo vệ. Đột nhiên một lập trình viên khác trong team vô tình viết:$ticket['balance'] = -9999;ở một file khác. Hệ thống sập, dữ liệu sai bét và bạn phải lục tung hàng ngàn dòng code để tìm xem hàm nào đã làm hỏng dữ liệu này.
Cách viết kiểu OOP (Lập trình hướng đối tượng)
Trong OOP, dữ liệu số dư (balance) bị khóa kín. Cách duy nhất để trừ tiền là yêu cầu chính chiếc vé đó thực hiện việc trừ tiền.
class MetroTicket {
private $id;
private $balance; // Bị khóa lại, không ai can thiệp được từ bên ngoài
private $status;
public function __construct($id, $balance) {
$this->id = $id;
$this->balance = $balance;
$this->status = 'active';
}
// Chiếc vé tự chịu trách nhiệm kiểm tra và trừ tiền của chính nó
public function deductFare($amount) {
if ($this->status !== 'active') {
throw new Exception("Vé không hợp lệ");
}
if ($this->balance >= $amount) {
$this->balance -= $amount;
return true;
}
return false;
}
public function getBalance() {
return $this->balance;
}
}
// Thực thi
$myTicket = new MetroTicket('TICK_999', 50000);
$myTicket->deductFare(15000);
Sức mạnh của OOP ở đây: Không một đoạn code nào bên ngoài có thể làm trò
$myTicket->balance = -9999vì thuộc tính này làprivate. ObjectMetroTickethoàn toàn làm chủ sinh mệnh và trạng thái của nó. Điều này giúp hệ thống cực kỳ bền bỉ và dễ gỡ lỗi khi mở rộng quy mô.
All rights reserved