Lập trình hướng đối tượng Pascal – Class
Bạn đã thấy các đối tượng Pascal phô bày một số đặc tính của mô hình hướng đối tượng. Chúng thực thi đóng gói, ẩn thông tin và thừa kế, nhưng chúng cũng có những hạn chế. Ví dụ các đối tượng Pascal không tham gia đa hình. Nên các lớp được sử dụng rộng rãi để thực thi chính xác hành vi hướng đối tượng trong chương trình. Nhất là phần mềm dựa trên GUI.
Một lớp được đĩnh nghĩa hầu như cúng một cách với đối tượng, nhưng là một con trỏ trỏ đến một đối tượng thích hợp hơn bản thân đối tượng. Về mặt kỹ thuật, điều này có nghĩa là lớp được cấp phát trên vùng nhớ Heap của chương trình, trong khi các đối tượng được cấp phát trên vùng Stack. Nói cách khác, khi bạn khai báo biến thuộc kiểu đối tượng, nó sẽ chiếm nhiều bộ nhớ trên Stack như kích thước của đối tượng, nhưng khi bạn khai báo biên thuộc kiểu lớp, nó sẽ luôn có kích thước của con trỏ trên Stack. Dữ liệu thực sự của lớp sẽ trên vùng Heap.
Đinh nghĩa lớp Pascal:
Một lớp được đĩnh nghĩa trong cùng một cách như đối tượng, sử dụng khai bái kiểu. Hình thức chung của một khai báo lớp như sau:
type class-identifier = class private field1 : field-type; field2 : field-type; ... public constructor create(); procedure proc1; function f1(): function-type; end; var classvar : class-identifier;
Có một số lưu ý sau:
- Định nghĩa lớp chỉ nên đặt dưới phần khai báo kiểu của chương trình.
- Một lớp được đĩnh nghĩa bằng từ khóa class.
- Các trường và dữ liệu tồn tại trong mỗi thực thê của lớp.
- Các phương thức được khai báo trong phần định nghĩa lớp.
- Có một hàm dựng đĩnh nghĩa trước gọi là Create trong lớp Gốc. Mỗi lớp trừu tượng và cụ thể là một con cháu của lớp Gốc, nên các lớp có ít nhất một hàm dựng.
- Có một hàm hủy đĩnh nghĩa trước gọi là Destroy trong lớp Gốc. Mỗi lớp trừu tượng và cụ thể là một con cháu của lớp Gốc, nên các lớp có ít nhất một hàm hủy.
Hãy địng nghĩa một lớp Rectangle có hai thành viên dữ liệu kiểu Integer – chiều dài và chiều rộng và một số hàm thành viên để xử lý những dữ liệu này và vẽ một hình chữ nhật.
type Rectangle = class private length, width: integer; public constructor create(l, w: integer); procedure setlength(l: integer); function getlength(): integer; procedure setwidth(w: integer); function getwidth(): integer; procedure draw; end;
Hãy viết một chương trình đầy đủ sẽ tạo một thực thể của Rectangle và vẽ hình chữ nhật. Nó cùng một ví dụ mà chúng toi sử dụng trong khi thảo luận ở phần Hướng đối tượng Pascal – Object. Bạn sẽ nhận thấy cả hai chương trình giống nhau, ngoại trừ những điểm sau đây:
- Bạn phải cần chỉ thị {$mode objfpc} để sực dụng các lớp.
- Bạn phải cần chỉ thị {$m+} để sực dụng các các hàm hủy.
- Thực thể lớp khác thực thể đối tượng. Chỉ khai báo biến nhưng không chiếm bộ nhớ cho thực thểm, mà bạn sẽ sử dụng hàm dựng để cấp phát bộ nhớ.
Đây là ví dụ đầu đủ:
{$mode objfpc} // directive to be used for defining classes {$m+} // directive to be used for using constructor program exClass; type Rectangle = class private length, width: integer; public constructor create(l, w: integer); procedure setlength(l: integer); function getlength(): integer; procedure setwidth(w: integer); function getwidth(): integer; procedure draw; end; var r1: Rectangle; constructor Rectangle.create(l, w: integer); begin length := l; width := w; end; procedure Rectangle.setlength(l: integer); begin length := l; end; procedure Rectangle.setwidth(w: integer); begin width :=w; end; function Rectangle.getlength(): integer; begin getlength := length; end; function Rectangle.getwidth(): integer; begin getwidth := width; end; procedure Rectangle.draw; var i, j: integer; begin for i:= 1 to length do begin for j:= 1 to width do write(' * '); writeln; end; end; begin r1:= Rectangle.create(3, 7); writeln(' Darw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth()); r1.draw; r1.setlength(4); r1.setwidth(6); writeln(' Darw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth()); r1.draw; end.
Khi đoạn mã trên được biên dịch và thực thi, nó sẽ tạo ra kết quả như sau:
Darw Rectangle: 3 by 7 * * * * * * * * * * * * * * * * * * * * * Darw Rectangle: 4 by 6 * * * * * * * * * * * * * * * * * * * * * * * *
Tính minh bạch của các thành viên của lớp:
Tính minh bạch chỉ ra khả năng truy cập đến thành viên của lớp. Các thành viên của lớp có 5 kiểu minh bạch:
Visibility | Accessibility |
---|---|
Public | Các thành viên luôn có thể được truy cập |
Private | Các thành viên chỉ có thể được truy cập trong mô đun hay đơn vị chứa định nghĩa lớp. Chúng có thể được truy cập từ bên trong các phương thức lớp hay từ bên ngoài chúng. |
Strict Private | Các thành viên chỉ có thể được truy cập từ các phương thức của chính lớp đó. Các lớp khác hay lớp con cháu trong cùng đơn vị không thể truy cập chúng. |
Protected | Nó giống như private, ngoại trừ, các thành viên này có thể được truy cập từ con cháu, thậm chí nếu nó được thực thi trong các mô đun khác. |
Published | Giống như public, nhưng trình biên dịch sinh ra loại thông tin đó là cần thiết cho tự động sắp xếp của các lớp nếu trình biên dịch trong chỉ thị {$M+}. Các trường được định nghĩa trong phần published phải là kiểu lớp. |
Constructors và Destructors của lớp Pascal:
Hàm hủy là một phương thức đặc biệt, được gọi tự động mỗi khi một đối tượng được tạo ra. Nên chúng tôi tận dụng điều này bằng việc khởi tạo nhiều thứ qua hàm tạo.
Pascal cung cấp một hàm đặc biệt gọi là create() để định nghĩa hàm dựng. Bạn có thể truyền nhiều đối số bạn muốn vào trong hàm dựng.
Ví dụ sau sẽ tạo một hàm dựng cho một lớp tên là Books và nó sẽ khởi tạo price( đơn giá) và title( tiêu đề) cho các book lúc đối tượng tạo ra.
program classExample; {$MODE OBJFPC} //directive to be used for creating classes {$M+} //directive that allows class constructors and destructors type Books = Class private title : String; price: real; public constructor Create(t : String; p: real); //default constructor procedure setTitle(t : String); //sets title for a book function getTitle() : String; //retrieves title procedure setPrice(p : real); //sets price for a book function getPrice() : real; //retrieves price procedure Display(); // display details of a book end; var physics, chemistry, maths: Books; //default constructor constructor Books.Create(t : String; p: real); begin title := t; price := p; end; procedure Books.setTitle(t : String); //sets title for a book begin title := t; end; function Books.getTitle() : String; //retrieves title begin getTitle := title; end; procedure Books.setPrice(p : real); //sets price for a book begin price := p; end; function Books.getPrice() : real; //retrieves price begin getPrice:= price; end; procedure Books.Display(); begin writeln('Title: ', title); writeln('Price: ', price:5:2); end; begin physics := Books.Create('Physics for High School', 10); chemistry := Books.Create('Advanced Chemistry', 15); maths := Books.Create('Algebra', 7); physics.Display; chemistry.Display; maths.Display; end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
Title: Physics for High School Price: 10 Title: Advanced Chemistry Price: 15 Title: Algebra Price: 7
Như hàm dựng ẩn có tên là create, đó cũng là một hàm hủy ẩn phá hủy bằng cách mà bạn có thể giải phóng các tài nguyên đã sử dụng trong lớp.
Inheritance:
Định nghĩa lớp Pascal có thể tùy ý thừa kế từ một lớp cha đã định nghĩa. Cú pháp như sau:
type childClas-identifier = class(baseClass-identifier) < members > end;
Ví dụ sau cung cấp một lớp novels, thừ kế từ lớp books và thêm nhiều hàm hơn dựa trên nhu cầu.
program inheritanceExample; {$MODE OBJFPC} //directive to be used for creating classes {$M+} //directive that allows class constructors and destructors type Books = Class protected title : String; price: real; public constructor Create(t : String; p: real); //default constructor procedure setTitle(t : String); //sets title for a book function getTitle() : String; //retrieves title procedure setPrice(p : real); //sets price for a book function getPrice() : real; //retrieves price procedure Display(); virtual; // display details of a book end; (* Creating a derived class *) type Novels = Class(Books) private author: String; public constructor Create(t: String); overload; constructor Create(a: String; t: String; p: real); overload; procedure setAuthor(a: String); // sets author for a book function getAuthor(): String; // retrieves author name procedure Display(); override; end; var n1, n2: Novels; //default constructor constructor Books.Create(t : String; p: real); begin title := t; price := p; end; procedure Books.setTitle(t : String); //sets title for a book begin title := t; end; function Books.getTitle() : String; //retrieves title begin getTitle := title; end; procedure Books.setPrice(p : real); //sets price for a book begin price := p; end; function Books.getPrice() : real; //retrieves price begin getPrice:= price; end; procedure Books.Display(); begin writeln('Title: ', title); writeln('Price: ', price); end; (* Now the derived class methods *) constructor Novels.Create(t: String); begin inherited Create(t, 0.0); author:= ' '; end; constructor Novels.Create(a: String; t: String; p: real); begin inherited Create(t, p); author:= a; end; procedure Novels.setAuthor(a : String); //sets author for a book begin author := a; end; function Novels.getAuthor() : String; //retrieves author begin getAuthor := author; end; procedure Novels.Display(); begin writeln('Title: ', title); writeln('Price: ', price:5:2); writeln('Author: ', author); end; begin n1 := Novels.Create('Gone with the Wind'); n2 := Novels.Create('Ayn Rand','Atlas Shrugged', 467.75); n1.setAuthor('Margaret Mitchell'); n1.setPrice(375.99); n1.Display; n2.Display; end.
Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:
Title: Gone with the Wind Price: 375.99 Author: Margaret Mitchell Title: Atlas Shrugged Price: 467.75 Author: Ayn Rand
Có một số điều quan trọng cần lưu ý như sau:
- Các thành viên của lớp books có thuộc tính protected.
- Lớp novels có hai hàm dựng, nên nạp chồng được sử dụng cho hàm nạp chồng.
- Thủ tục books.display đã được khai báo virtual, nên cùng phương thức từ lớp novels có thể override(ghi đè) lên nó.
- Hàm dựng novels.create gọi hàm dựng lớp lớp cơ sở sử dụng từ khóa inherited.
Interfaces(phần chung):
Phần chung được định nghĩa để cung cấp tên hàm phổ biến tới phần nội dung. Các phần nội dung khác nhau có thể thực thi những phần chung theo nhu cầu của chúng. Bạn có thể nói, phần chung là bộ xương, được thực thi bởi người lập trình. Sau đây là một ví dụ của phần chung:
type Mail = Interface Procedure SendMail; Procedure GetMail; end; Report = Class(TInterfacedObject, Mail) Procedure SendMail; Procedure GetMail; end;
Xin lưu ý rằng, khi một lớp thực thi một phần chung, nó sẽ thực thi tất cả phương thức của phần chung. Nếu phương thức của một phần chung
không được thực thi, sau đó trình biên dịch sẽ báo lỗi.
type Shape = ABSTRACT CLASS (Root) Procedure draw; ABSTRACT; ... end;
Khi sự thừa kế từ một lớp trừu tượng, tất cả phương thức đánh dấu trừu tượng trong phần khai báo của lớp cha phải được định nghĩa bởi lớp con; ngoài ra, những phương thức này phải được định nghĩa cùng một tính minh bạch.
Từ khóa Static:
Khai báo các thành viên lớp hay phương thức là tĩnh làm chúng truy cập không cần một thực thể của lớp. Một thành viên khai báo tĩnh không thể được truy cập với một đối tượng thực thể hóa từ lớp(mặc dù phương pháp tĩnh có thể). Ví dụ sau minh họa khái niệm này:
{$mode objfpc} {$static on} type myclass=class num : integer;static; end; var n1, n2 : myclass; begin n1:= myclass.create; n2:= myclass.create; n1.num := 12; writeln(n2.num); n2.num := 31; writeln(n1.num); writeln(myclass.num); myclass.num := myclass.num + 20; writeln(n1.num); writeln(n2.num); end.
Khi đoạn mã trên được biên dịch và thực thi, nó sẽ tạo ra kết quả sau:
12 31 31 51 51
‹ 5 Cách để học lập trình nhanh hơn Download Free Pascal ›