CONCRETE CLASS: TỪ BẢN VẼ TRỪU TƯỢNG ĐẾN THỰC THỂ SỐNG
khi bước chân vào thế giới của Lập trình Hướng Đối Tượng (OOP) nâng cao và các mẫu thiết kế kiến trúc (Design Patterns), bạn sẽ liên tục nghe đến hai khái niệm đối lập nhưng bổ trợ cho nhau: Interface (Giao diện) và Concrete Class (Lớp cụ thể).
Nếu Interface là "bản thiết kế" (blueprint) nằm trên giấy, thì Concrete Class chính là "ngôi nhà thực tế" được xây lên bằng gạch và bê tông.
Hãy cùng mổ xẻ xem Concrete Class thực chất là gì và tại sao nó lại đóng vai trò không thể thay thế trong các ứng dụng Enterprise.
1. Bản Chất Của Concrete Class Là Gì? (The What)
Trong lập trình hướng đối tượng, Concrete Class (Lớp cụ thể) là một class bình thường mà bạn có thể trực tiếp khởi tạo ra đối tượng (object) bằng từ khóa new (hoặc thông qua Dependency Injection Container).
Nó trái ngược hoàn toàn với hai khái niệm:
-
Abstract Class (Lớp trừu tượng): Chứa các hàm chưa có phần thân, không thể dùng từ khóa
newđể khởi tạo trực tiếp mà bắt buộc phải có class con kế thừa. -
Interface (Giao diện): Chỉ là một tập hợp các chữ ký hàm (method signatures) rỗng, không chứa bất kỳ logic code hay thuộc tính thực tế nào.
Ví dụ trực quan:
PHP
// 1. INTERFACE: Chỉ là bản cam kết (Phải có hàm charge tiền)
interface PaymentGatewayInterface
{
public function charge(float $amount): bool;
}
// 2. CONCRETE CLASS: Đây mới là hiện thân thực tế có code chạy thật
class StripePaymentGateway implements PaymentGatewayInterface
{
public function charge(float $amount): bool
{
// Code kết nối API của Stripe thật để trừ tiền...
return true;
}
}
Trong ví dụ trên, StripePaymentGateway chính là một Concrete Class. Bạn có thể dùng từ khóa new StripePaymentGateway() hoặc tiêm nó vào Service để chạy code thực tế.
2. Vai Trò Của Concrete Class Trong Clean Architecture
Trong các hệ thống lớn áp dụng Dependency Inversion Principle (DIP) (Nguyên tắc đảo ngược phụ thuộc — chữ D trong SOLID), chúng ta thường viết code dựa trên các Interface để giảm độ gắn kết (loose coupling). Tuy nhiên, hệ thống không thể chỉ tồn tại toàn các bản vẽ trừu tượng. Cuối cùng, nó vẫn phải hạ cánh xuống các Concrete Class.
Sự phân chia quyền lực giữa Interface và Concrete Class diễn ra như sau:
-
Business Logic (Tầng nghiệp vụ): Chỉ nói chuyện với Interface (Ví dụ: "Tôi không quan tâm ông dùng Stripe hay Momo, miễn là ông đưa cho tôi một cái
PaymentGatewayInterfaceđể tôi gọi hàmcharge()"). -
Infrastructure (Tầng ngoại biên): Chứa các Concrete Class thực hiện chi tiết việc kết nối cơ sở dữ liệu, gọi API bên thứ ba, hay gửi mail thực tế.
3. Concrete Class Trong Hệ Sinh Thái Laravel
Trong Laravel, hầu hết các Class bạn viết hằng ngày đều là Concrete Class:
-
Các Controllers (
UserController) xử lý HTTP Request. -
Các Services (
PickupLocationAddressV2ConvertService) chứa logic tính toán. -
Các Repositories xử lý câu lệnh Eloquent Query.
-
Ngay cả các Eloquent Models (
class PickupLocation extends Model) cũng là các Concrete Class mang đầy đủ dữ liệu thực tế từ Database lên RAM.
Khi Laravel Service Container thực hiện hành động Autowiring, nó tìm kiếm Concrete Class tương ứng để khởi tạo:
PHP
// Khi bạn gọi dòng này, Laravel Container tự động tìm Concrete Class 'OrderService'
// và dùng từ khóa new để dựng nó lên cho bạn.
$service = app(OrderService::class);
4. Những Cái Bẫy Cần Tránh Khi Lạm Dụng Concrete Class
Mặc dù Concrete Class rất quen thuộc, nhưng nếu thiết kế hệ thống mà phụ thuộc trực tiếp vào Concrete Class thay vì Interface, bạn sẽ rơi vào các hiểm họa sau:
-
Cứng nhắc (Tight Coupling): Nếu
OrderServicecủa bạn trực tiếpnew StripePaymentGateway()bên trong ruột của nó, sau này sếp bắt đổi sang Momo, bạn sẽ phải vào sửa code củaOrderService. Điều này vi phạm nghiêm trọng Open/Closed Principle (OCP). -
Khó viết Unit Test: Khi bạn muốn viết Unit Test cho một Service mà service đó cứ đi gọi trực tiếp các Concrete Class kết nối Database hoặc gọi API thật, test của bạn sẽ cực kỳ chậm và dễ gãy. Giải pháp là ta sẽ "mock" các Interface chứ không mock trực tiếp Concrete Class.
💡 Lời Kết
Concrete Class chính là "gạch đá thực tế" để xây dựng nên phần mềm của bạn. Hãy sử dụng Interface để thiết kế khung sườn và quy tắc giao tiếp giữa các module, nhưng hãy dùng Concrete Class để hiện thực hóa các dòng code chạy thật phía bên dưới. Sự kết hợp nhịp nhàng giữa hai khái niệm này là chìa khóa để tạo nên một kiến trúc phần mềm vừa linh hoạt, vừa mạnh mẽ!
All Rights Reserved