Học Pascal

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

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Free Web Hosting