Tính Trừu Tượng Abstraction
được Ada 95
hỗ trợ với khái niệm Interface
– được hiểu là giao diện lập trình. Khái niệm này cũng xuất phát từ cuộc sống hàng ngày khi mà chúng ta có các đồ vật có thể được thiết kế với một hoặc nhiều nhóm tính năng. Ví dụ như một chiếc USB lưu trữ được thiết kế kiêm một chiếc card-wifi
thì sẽ có hai giao diện sử dụng là duyệt tệp lưu trữ và bảng điều khiển chức năng kết nối.
Interface
Một object
bất kỳ trong môi trường phần mềm cũng có thể được thiết kế như vậy, và nhiều object
khác nhau có thể sẽ có cùng giao diện chức năng. Khái niệm Interface
được sử dụng để tạo ra một bản khai báo ở dạng thiết kế trừu tượng về một giao diện chức năng. Sau đó, các kiểu dữ liệu khác nhau có thể chọn khai báo để triển khai Interface
này bằng một cú pháp lệnh ngắn gọn.
Và kết quả là trình biên dịch sẽ không dừng nhắc nhở cho đến khi các kiểu dữ liệu có khai báo Interface
này đã được viết đầy đủ code triển khai chi tiết cho các sub-program
có tên được liệt kê trong định nghĩa của Interface
.
package Being is
type Entity is interface;
type Class is access all Entity'Class;
procedure Meditate (Self: in Entity) is abstract;
end Being;
Ở đây chúng ta đang định nghĩa kiểu Being.Entity
là một interface
và không có mô tả chi tiết. Vì vậy nên các primitive
hay các sub-program
khác có sử dụng các tham số kiểu này sẽ không thể viết logic xử lý chi tiêt được và đều phải dừng lại ở cấp độ khai báo trừu tượng is abstract
.
Khi chúng ta sử dụng kiểu Being.Entity
trong thao tác kế thừa của một kiểu tagged record
nào đó thì chúng ta sẽ có thể nhờ trình biên dịch nhắc nhở cho đến khi các sub-program
này được triển khai đầy đủ code chi tiết ở kiểu kế thừa.
Bây giờ chúng ta sẽ thử khai báo kiểu Coder
có áp dụng interface
này và chạy code trước khi bổ sung thêm procedure Meditate
.
with Person; use Person;
with Being; use Being;
package Person.Coder is
-- ...
type Entity is
new Person.Entity
and Being.Entity
with record
Intellect : Bit;
end record;
-- ...
end Person.Coder;
alr run
error: type must be declared abstract or "Meditate" overridden
error: "Meditate" has been inherited from subprogram at being.ads
compilation of main.adb failed
Thông báo này nhắc nhở chúng ta là kiểu Coder.Entity
đang kế thừa kiểu Being.Entity
là một interface
và cần phải override
phương thức Meditate
, hoặc chuyển thành định nghĩa kiểu trừu tượng abstract
.
with Ada.Text_IO; use Ada.Text_IO;
with Person; use Person;
package body Person.Coder is
-- ...
overriding
procedure Meditate (Self: in Entity) is
-- local
begin
Put_Line ("Meditating...");
end Meditate;
end Person.Coder;
Bây giờ chúng ta sẽ thử tham chiếu tới một object Coder.Entity
bằng con trỏ Being.Class
và truy xuất thuộc tính Name
.
with Ada.Text_IO; use Ada.Text_IO;
with Person; use Person;
with Person.Coder; use Person.Coder;
with Being; use Being;
procedure Main is
Me : Being.Class;
begin
Me := new Coder.Entity'
( Name => "Semi Dev_ "
, Intellect => 0 );
Put_Line ("My name: " & Me.Name);
end Main;
alr run
error: no selector "Name" for type "Entity'Class" defined at being.ads
Do chúng ta đang sử dụng một con trỏ kiểu access all Being.Entity'Class
, lúc này tất cả những gì mà con trỏ này có thể nhìn thấy là các thành phần trong giao diện interface Being
. Tất cả các khía cạnh khác của object Coder.Entity
vẫn ở đó nhưng chỉ đơn giản là chúng ta không thể nhìn thấy từ góc nhìn của Being.Entity
.
Ở đây chúng ta lưu ý là Ada
chỉ hỗ trợ kế thừa từ duy nhất một kiểu record
có định nghĩa chi tiết, và cho phép áp dụng nhiều interface
bằng các từ khóa and
.
type Coder is
new Person
and Being
and Living
and Thinking
and ...
with record
-- fields ...
end record;
Tính năng này cũng hoàn toàn tương tự với các ngôn ngữ lập trình OOP
phổ biến hiện tại.
Abstract record
Oh.. như vậy là chúng ta còn có thêm một yếu tố nữa xuất hiện trong thông báo lỗi của trình biên dịch; Đó là chúng ta có thể định nghĩa một kiểu record
trừu tượng abstract
. Khái niệm này tương đương với abstract class
trong các ngôn ngữ lập trình OOP
phổ biến.
Định nghĩa abstract record
có thể có code triển khai chi tiết, tuy nhiên chúng ta sẽ không thể tạo ra object
từ cú pháp new
như với các kiểu record
thông thường. Thêm vào đó là bởi vì các abstract record
có thể được định nghĩa chi tiết hoặc chỉ khai báo đơn giản giống như interface
nên các sub-program
liên quan cũng có thể có code triển khai chi tiết hoặc là phải khai báo abstract
.
Trong trường hợp cần tạo ra một bản mẫu cho nhiều kiểu record
mô phỏng các thực thể có nhiều điểm chung, chúng ta có thể nghĩ tới việc sử dụng abstract record
để giảm thiểu lượng code định nghĩa lặp, và hiển nhiên là cả các sub-program
xoay quanh abstract record
cũng vậy.
Bây giờ chúng ta sẽ chuyển kiểu Person.Entity
thành abstract record
và khai báo thêm một procedure Learn
ở dạng abstract
chưa có code triển khai.
package Person is
type Entity is abstract tagged record
Name : String (1 .. 12);
end record;
procedure Learn (Self: in Entity) is abstract;
end Person;
Code sử dụng tại main
sẽ chỉ đơn giản là cố gắng tạo ra object
từ abstract record
này.
with Ada.Text_IO; use Ada.Text_IO;
with Person; use Person;
procedure Main is
Me : Person.Class;
begin
Me := new Person.Entity'
(Name => "Semi Dev_ ");
Put_Line ("My name: " & Me.Name);
end Main;
alr run
main.adb: error: cannot allocate abstract object
person-coder.ads: error: type must be declared abstract or "Learn" overridden
person-coder.ads: error: "Learn" has been inherited from subprogram at person.ads
Thông báo lỗi đầu tiên là ở code sử dụng tại main
, thao tác tạo object
từ kiểu abstract
là không hợp lệ; Và các thông báo lỗi tiếp theo là ở package Person.Coder
chúng ta đang có kiểu Coder.Entity
kế thừa từ abstract Person.Entity
nhưng chưa triển khai đủ các sub-program abstract
liên quan, cụ thể là procedure Learn
vừa định nghĩa.
Bổ sung khai báo procedure Learn
và code triển khai cho package Person.Coder
.
with Person; use Person;
with Being; use Being;
package Person.Coder is
-- ...
type Entity is
new Person.Entity
and Being.Entity
with record
Intellect : Bit;
end record;
overriding
procedure Learn (Self: in Entity);
-- ...
end Person.Coder;
with Ada.Text_IO; use Ada.Text_IO;
with Person; use Person;
package body Person.Coder is
-- ...
overriding
procedure Learn (Self: in Entity) is
-- local
begin
Put_Line ("Learning...");
end Learn;
end Person.Coder;
Ở code sử dụng tại main
chúng ta sẽ sửa lại thao tác khởi tạo object
từ kiểu Coder.Entity
và gọi phương thức Learn
vừa định nghĩa bổ sung.
with Ada.Text_IO; use Ada.Text_IO;
with Person; use Person;
with Person.Coder; use Person.Coder;
procedure Main is
Me : Person.Class;
begin
Me := new Coder.Entity'
( Name => "Semi Dev_ "
, Intellect => 0 );
Me.Learn;
end Main;
alr run
Learning...
[Object-Oriented + Ada] Bài 5 – Encapsulation & Package Privacy
Nguồn: viblo.asia