Lưu ý: Interface trong bài có nghĩa là giao diện, nhưng không nên dịch nghĩa sang tiếng Việt từ này nên tôi sẽ dùng nguyên gốc từ interface.
Lấy ví dụ, bạn có các lớp TKem (kem), TDat (đất), TKhongKhi (không khí). Bây giờ, bạn muốn thêm một phương thức function CanEat: boolean
vào mỗi class để kiểm tra xem có ăn được hay không. Bạn sẽ làm như thế nào ?
Bạn có thể thêm vào từng khai báo lớp, nhưng như thế sẽ rất mất thời gian. Nếu bạn có 50 lớp như thế, thời gian sẽ tăng lên rất nhiều. Bạn cần phải tìm một cách giải quyết khác.
Interface ra đời cho mục đích đó. Nó giúp tiết kiệm thời gian và công sức. Interface tuyên bố những phương thức chung. Nếu một class được gắn thêm một interface, nghĩa là class đó bắt buộc phải có những gì interface có.
Ví dụ sau, chúng ta khai báo một interface đơn giản IAction (mọi interface nên được đặt tên với tiền tố I)
type
IAction = interface
['{CFC7B260-A46F-4FF3-8F93-FAB522393B9F}']
procedure Eat;
function CanThink: boolean;
end;
Khai báo một interface tương tự như class. Interface trên gồm hai phương thức Eat (ăn) và CanThink (có thể suy nghĩ).
Lưu ý: Một interface không thể chứa các trường (fields), chỉ có thể chứa phương thức và thuộc tính (phải có Getter và Setter). Interface không thể phân quyền truy cập (visibility level) như class, mọi thứ đặt trong interface đều public.
Chú ý đến dòng thứ 3 trong code trên, có một giá trị string được đặt trong cặp ngoặc vuông [ và ].
Đó gọi là GUID, nó xác định một interface duy nhất trong hệ thống. Hai interface khác nhau không thể có cùng một GUID.
Làm cách nào để có giá trị GUID này, rất đơn giản. Chỉ cần nhấn tổ hợp phím Ctrl + Shift + G, Delphi sẽ tự động chèn GUID vào vị trí con trỏ hiện tại.
Sau đó, chúng ta có thể gắn interface trên vào class
type
TDog = class(TIInterfacedObject, IAction)
procedure Sleep;
end;
TNguoi = class(TInterfacedObject, IAction)
procedure LamViec;
end;
Một khi đã gắn interface vào class, thì mọi thứ thuộc interface đều thuộc class, như vậy có nghĩa chúng ta phải triển khai tất cả các phương thức trong interface cho từng class khác nhau.
// Triển khai phương thức có sẵn của class
procedure TDog.Sleep;
begin
WriteLn('Dog sleeping');
end;
procedure TNguoi.LamViec;
begin
WriteLn('Lam viec 8h');
end;
// Triển khai phương thức cho Interface
function TDog.CanThink: boolean;
begin
Result := false; // Chó không thể suy nghĩ
end;
function TNguoi.CanThink: boolean;
begin
Result := true; // Người có thể suy nghĩ
end;
procedure TDog.Eat;
begin
WriteLn('Dog eat bone');
end;
procedure TNguoi.Eat;
begin
WriteLn('Nguoi eat foods');
end;
Mọi class được gắn Interface đều phải thuộc lớp cơ sở là TInterfacedObject để sử dụng các hàm _Addref, _Release, ...
Một trong những sức mạnh của Interface là có thể dùng class để khởi tạo Interface. Sử dụng ví dụ Dog và người ở trên.
var
Obj: IAction;
begin
Obj := TDog.Create;
Obj.Eat;
Obj := TNguoi.Create;
Obj.Eat;
end.
Đoạn chương trình trên khi chạy sẽ cho ra output
Dog eat bone
Nguoi eat foods
Như đã thấy, một interface có thể khởi tạo bằng một class. Chỉ với một lệnh Obj.Eat
, tùy vào ngữ cảnh (tùy vào class dùng để khởi tạo interface) mà các phương thức khác của Interface được sử dụng phù hợp.
Điển hình cho việc này là ITask trong unit System.Threading, xem thêm bài viết Parallel Programing