Lập trình hướng đối tượng trong Dart
- 23-05-2024
- Toanngo92
- 0 Comments
Mục lục
OOP trong Dart
OOP là một phương pháp lập trình trong đó các đối tượng được sử dụng thay vì logic. Nó tích hợp các khái niệm như Trừu tượng hóa, Đóng gói, Đa hình và Kế thừa bằng cách sử dụng các lớp, đối tượng và các tính năng khác. Mục tiêu của OOP là giảm độ phức tạp của lập trình và thực hiện nhiều nhiệm vụ đồng thời.
Lớp trong Dart
Lớp là các loại dữ liệu do người dùng định nghĩa mô tả hành vi và đặc điểm của một thực thể. Một thực thể có thể là bất kỳ đối tượng trong thế giới thực nào. Ví dụ, sách, ô tô, cá nhân, và những vật tương tự có thể được xem là các thực thể. Do đó, mỗi thực thể này có thể được đại diện bằng các lớp.
Một lớp bao gồm các thành viên dữ liệu và hàm thành viên. Từ khóa class được sử dụng trong Dart để tạo ra một lớp. Định nghĩa của một lớp bắt đầu với từ khóa class, tên lớp và cơ thể của lớp được bao quanh bởi dấu ngoặc nhọn.
Cú pháp của định nghĩa lớp như sau:
class ClassName {
}
Object
Khi một thể hiện của một lớp được tạo ra, nó được gọi là một Đối tượng. Trong Dart, có thể tạo và sử dụng nhiều đối tượng của một lớp.
Nên sử dụng tên có ý nghĩa cho các lớp và đối tượng. Ví dụ, Employee có thể là tên lớp và emp1 và emp2 có thể là các đối tượng. Cú pháp để tạo đối tượng như sau:
ClassName ObjectName = ClassName();
Lưu ý: Mặc dù nhiều ngôn ngữ lập trình hỗ trợ việc sử dụng từ khóa new để tạo đối tượng, trong Dart, nó là tùy chọn và có thể bị bỏ qua.
Biến Thể và Biến Lớp
Các biến liên quan đến lớp có thể được phân loại thành biến thể và biến lớp. Bảng bên dưới xác định và liệt kê sự khác biệt giữa biến thể và biến lớp:
Biến thể | Biến lớp |
Là một biến lớp được chia sẻ bởi tất cả các thể hiện của lớp. | Là một biến tĩnh được khai báo bên trong một lớp. |
Có thể có các giá trị khác nhau cho mỗi đối tượng. | Chỉ có thể có một giá trị và được chia sẻ giữa tất cả các đối tượng. |
Giá trị được giữ lại miễn là đối tượng đó hoạt động. | Giá trị được giữ lại cho đến khi chương trình kết thúc. |
Biến Thể: Đây là một biến lớp được chia sẻ bởi tất cả các thể hiện của lớp. Nó có thể có các giá trị khác nhau cho mỗi đối tượng và giá trị được giữ lại cho đến khi đối tượng hoạt động.
Biến Lớp: Đây là một biến tĩnh được khai báo bên trong một lớp. Nó chỉ có một giá trị và được chia sẻ giữa tất cả các đối tượng, và giá trị được giữ lại cho đến khi chương trình kết thúc.
// Ví dụ về biến thể
class NewClass {
int counter;
}
// Ví dụ về biến lớp
class NewClass {
static int counter;
}
Các Phương thức trong Dart
Các nhà phát triển có thể tạo ra các phương thức của riêng họ trong Dart. Phương thức là một bộ câu lệnh thực hiện một số chức năng liên quan đến thực thể cho mà một lớp được định nghĩa. Phương thức giúp chia nhỏ các chương trình lớn thành các phần nhỏ hơn, từ đó tăng khả năng tái sử dụng của mã. Các phương thức có thể được gọi khi cần thiết. Thông tin có thể được truyền qua các phương thức với sự giúp đỡ của tham số. Chúng có thể hoặc không trả về bất kỳ giá trị nào.
Phương thức Lớp (Phương thức Tĩnh)
Để khai báo một phương thức lớp, cũng được gọi là phương thức tĩnh, từ khóa static được sử dụng. Một phương thức tĩnh không thể truy cập thông qua một đối tượng hoặc thể hiện. Điều này loại bỏ sự cần thiết phải tạo một đối tượng mới mỗi lần sử dụng phương thức, từ đó tối ưu hóa mã. Một phương thức tĩnh được khai báo bằng cách sử dụng từ khóa static, tên phương thức và kiểu trả về.
static Returntype methodName()
{
// statement
}
Một ví dụ về việc sử dụng phương thức tĩnh/lớp
class Student {
var name = "Adelina";
static void printName(name) {
print(name);
}
}
void main() {
Student.printName("Eliza");
}
Trong Mã đoạn mã trên, một lớp Student được tạo ra. Trong lớp đó, một biến name
được tạo ra. Một phương thức tĩnh/lớp printName()
được khai báo là tĩnh với void
làm kiểu trả về, chỉ ra rằng phương thức sẽ không trả về bất cứ thứ gì.
Phương thức printName()
nhận name
làm đối số. Cuối cùng, trong hàm main()
, phương thức tĩnh printName()
được gọi trực tiếp mà không cần tạo một đối tượng của một lớp và tên được in ra trên cửa sổ console
như thể hiện trong Hình bên dưới.
Phương thức Thể hiện
Phương thức Thể hiện là những phương thức chỉ có thể truy cập thông qua việc sử dụng một đối tượng của lớp. Phương thức Thể hiện cũng có thể được truy cập trong một lớp thông qua từ khóa this, ngụ ý một đối tượng vô danh không được gán tên.
Một phương thức sẽ có một định nghĩa, một thân, và một cuộc gọi phương thức. Một phương thức thể hiện được định nghĩa với một tên, một kiểu trả về, và một danh sách các tham số. Điều này được gọi là chữ ký của nó.
Cú pháp của việc tạo một phương thức thể hiện với chữ ký và thân là như sau:
Returntype methodName(arguments)
{
//statement body
}
Một phương thức thể hiện có thể được ‘gọi’ hoặc ‘kích hoạt’ bởi một đối tượng ở bất kỳ đâu trong chương trình, miễn là đối tượng đã được định nghĩa.
Một ví dụ về việc sử dụng một phương thức thể hiện:
class Student {
var name = "Adelina";
void printName(String name) {
print(name);
}
}
void main() {
Student im = new Student();
im.printName("Julia");
}
Trong Đoạn mã trên, một lớp Student
được tạo ra. Trong lớp này, một biến tên name
được tạo ra. Phương thức printName()
nhận tham số name
và in ra tên. Cuối cùng, trong hàm main
, một đối tượng của lớp được tạo và sử dụng đối tượng đó, tên được in ra trong terminal như được hiển thị trong Hình bên dưới.
Trong các ứng dụng thực tế, một lớp thường sẽ có nhiều phương thức thực thể và cũng có nhiều đối tượng.
Phương Thức Getter và Setter trong Dart
Các phương thức Getter và Setter trong Dart được sử dụng để lấy và đặt giá trị cho các biến của lớp tương ứng. Bảng bên dưới liệt kê sự khác biệt giữa các phương thức Getter và Setter.
Phương thức | Mô tả | Cú pháp |
Getter | Được sử dụng để lấy giá trị của một trường cụ thể của lớp. Dữ liệu chỉ có thể được đọc và không thể sửa đổi bằng phương thức get. | Dart returnType get fieldName { ... } |
Setter | Được sử dụng để đặt hoặc ghi đè giá trị của một biến đã được lấy bằng phương thức get. | Dart set fieldName(value) { ... } |
Một ví dụ về việc sử dụng các phương thức Getter và Setter được hiển thị trong Đoạn mã bên dưới.
class Employee {
late String eName;
String get ename {
return eName;
}
set ename(String name) {
this.eName = name;
}
}
void main() {
Employee emp = Employee();
emp.ename = 'Jeff';
print("Employee name is: ${emp.ename}");
}
Trong Đoạn mã bên trên, một lớp Employee
được tạo ra. Một biến eName
được định nghĩa là một chuỗi, từ khóa get
được sử dụng để lấy giá trị của eName
. Từ khóa set
được sử dụng để đặt giá trị cho biến eName
. Sau đó, trong hàm main
, một đối tượng của lớp Employee
được tạo ra. Tên của nhân viên được in ra bằng cách sử dụng phương thức print()
như được hiển thị trong Hình bên dưới.
Áp Dụng Các Khái Niệm Lập Trình Hướng Đối Tượng Trong Dart
Dart là một ngôn ngữ lập trình hướng đối tượng (OOP) hỗ trợ tất cả các khái niệm của OOP như lớp, đối tượng, kế thừa, đa hình, giao diện, và trừu tượng. Khi các khái niệm này được hiểu rõ, chúng có thể được áp dụng dễ dàng trong Dart.
Các từ khóa sau đây là quan trọng trong việc áp dụng các khái niệm OOP: class
, implements
, extends
, @override
, và v.v.
Kế Thừa
Khái niệm một lớp kế thừa các thuộc tính và phương thức của một lớp khác được gọi là Kế thừa. Lớp chính có các thuộc tính và phương thức được gọi là lớp cha và lớp kế thừa từ lớp cha được gọi là lớp con. Từ khóa extends
được sử dụng để kế thừa các thuộc tính từ lớp cha sang lớp con.
Có hai loại kế thừa trong Dart:
Kế thừa đơn cấp:
Trong kế thừa đơn cấp, một lớp con được kế thừa từ một lớp cha.
class ParentClass {
// Các thuộc tính và phương thức của lớp cha
}
class ChildClass extends ParentClass {
// Kế thừa các thuộc tính và phương thức từ lớp cha
}
Kế thừa đa cấp:
Trong kế thừa đa cấp, một lớp con được kế thừa từ một lớp con khác.
class ParentClass {
// Các thuộc tính và phương thức của lớp cha
}
class ChildClass1 extends ParentClass {
// Kế thừa các thuộc tính và phương thức từ lớp cha
}
class ChildClass2 extends ChildClass1 {
// Kế thừa các thuộc tính và phương thức từ lớp con khác
}
Đa Hình
Đa hình đề cập đến khái niệm một đối tượng có nhiều hình thức. Về mặt kỹ thuật, đa hình là mở rộng và sửa đổi các tính năng của lớp con bằng cách sử dụng các tính năng đã có sẵn của lớp cha. Điều này giúp tiết kiệm thời gian và giảm số dòng mã cũng như công sức.
Dart hỗ trợ đa hình thông qua ghi đè phương thức. Một phương thức trong lớp cha có thể được ghi đè trong lớp con.
Cú pháp của Đa Hình bằng cách Ghi Đè Phương Thức:
class ParentClass {
void parentMethod() {
// something
}
}
class ChildClass extends ParentClass {
@override
void parentMethod() {
// something
}
}
Interfaces
Interface là một bản thiết kế của một lớp. Trong một interface, chỉ có khai báo trừu tượng của các phương thức.
Dart không có từ khóa interface
. Mỗi lớp ngầm định định nghĩa một interface. Điều này có nghĩa là các lập trình viên có thể triển khai bất kỳ lớp nào.
Trong Dart, nếu một lớp được kế thừa, thì tất cả các phương thức từ lớp kế thừa phải được định nghĩa lại. Để thực thi điều này và đảm bảo nó được thực hiện, từ khóa implements
được sử dụng.
Cú pháp của Interface:
class InterfaceClassName {
ReturnType method() {
//some functionality
}
}
class ClassName implements InterfaceClassName {
@override
ReturnType method() {
//different functionality than that which was defined in
//parent class
}
}
Abstraction
Trong lập trình hướng đối tượng (OOP), trừu tượng hóa có nghĩa là ẩn đi những thông tin không cần thiết và chỉ hiển thị những thông tin quan trọng cho người dùng. Điều này làm giảm độ phức tạp của lập trình. Ví dụ, kiến thức về quy trình backend để gửi một tin nhắn không cần thiết cho người dùng để gửi một tin nhắn.
Trong Dart, các lớp trừu tượng được tạo bằng từ khóa abstract
. Chúng có các phương thức có thể hoặc không có bất kỳ triển khai nào. Các lớp kế thừa từ lớp trừu tượng này có thể ghi đè các phương thức trừu tượng. Một lớp trừu tượng cũng có thể được kế thừa.
Ví dụ về Lớp Trừu Tượng:
abstract class MainClass {
void abstractPrintMethod();
}
class ImplementationClass implements MainClass {
@override
void abstractPrintMethod() {
// do something
}
}
Constructors trong Dart
Constructors là các phương thức đặc biệt có cùng tên với lớp và được sử dụng để khởi tạo một đối tượng. Bất cứ khi nào một đối tượng được tạo trong chương trình, nó sẽ tự động gọi constructor.
Cú pháp của Constructor:
class ClassName {
ClassName() {
// constructor
}
}
Các Loại Constructor trong Dart
Có ba loại constructor bao gồm:
Default constructors là những constructor không có bất kỳ tham số nào. Chúng còn được gọi là constructors không có tham số.
Một ví dụ về default constructor được hiển thị trong Đoạn mã bên dưới.
void main() {
Student std = Student();
}
class Student {
Student() {
print("Default Constructor");
}
}
Trong Đoạn mã trên, một đối tượng có tên là std
được tạo trong hàm main()
. Trong lớp Student
, một constructor mặc định được tạo và câu lệnh print()
được sử dụng trong constructor đó để in ra “Default Constructor” trong terminal như hiển thị trong Hình bên dưới.
Parameterized Constructor
Parameterized constructors là những constructor có tham số, thông qua đó chúng nhận một số biến làm đối số. Điều này giúp xác định constructor nào sẽ được gọi.
Một ví dụ về parameterized constructor được hiển thị trong Đoạn mã bên dưới.
class Student {
Student(String name) {
print("Student name is: ${name}");
}
}
void main() {
Student std = Student("Ray");
}
Trong Đoạn mã trên, một lớp Student
được định nghĩa với một parameterized constructor trong đó một đối số chuỗi name
được truyền vào. Sau đó, sử dụng câu lệnh print()
, tên được in ra trong terminal.
Một đối tượng của lớp Student
với tên std
được tạo trong hàm main()
. Parameterized constructor của Student
được gọi khi tạo đối tượng và một tham số tên được truyền vào với giá trị “Ray”. Điều này dẫn đến kết quả như hiển thị trong Hình bên dưới.
Named Constructor
Named Constructor được sử dụng để tạo nhiều constructor với các tên khác nhau trong cùng một lớp.
Một ví dụ về named constructor được hiển thị trong Đoạn mã bên dưới.
void main() {
Student std1 = Student();
Student std2 = Student.namedConst("Computer Science");
}
class Student {
Student() {
print("Default constructor");
}
Student.namedConst(String branch) {
print("Branch name is: ${branch}");
}
}
Trong Đoạn mã trên, hai đối tượng được tạo trong hàm main()
cho lớp Student
. Hai constructor được tạo trong lớp Student
; một là constructor mặc định và một là named constructor.
Constructor mặc định không nhận tham số nào, trong khi đó, cho constructor có tên, một tham số chuỗi được gọi là “branch” được truyền vào. Cuối cùng, một câu lệnh print()
được sử dụng để in ra “branch” trong terminal như được hiển thị trong Hình bên dưới.
Từ khóa this
, static
, và super
trong Dart và cách sử dụng của chúng
Từ khóa this
Từ khóa this
được bao gồm khi các tham số và thuộc tính có cùng tên với các thuộc tính của lớp. Nó tham chiếu đến đối tượng lớp hiện tại và từ khóa this
loại bỏ sự mơ hồ phát sinh khi khai báo cùng tên cho các tham số và thuộc tính.
Một ví dụ về từ khóa this
được hiển thị trong Đoạn mã bên dưới.
void main() {
Student st = Student(1101);
}
class Student {
var stid;
Student(var stid) {
this.stid = stid;
print("Dart this keyword Example");
print("Student ID is: ${stid}");
}
}
Lớp Student
chứa một trường gọi là stid. Một constructor có tham số được sử dụng trong đó stid
được lấy làm tham số. Mã trong constructor sẽ được thực thi trong quá trình tạo một đối tượng thể hiện của lớp Student
.
Trong hàm main(),
một đối tượng thể hiện cho lớp Student
được tạo với stid
được truyền làm đối số. Khi mã được thực thi, các câu lệnh trong constructor của Student
được thực thi. Kết quả cho Đoạn mã bên dưới được hiển thị trong hình:
Từ khóa super
Từ khóa super
được sử dụng để tham chiếu đến đối tượng của lớp cha. Các phương thức và thuộc tính của lớp cha có thể được gọi bằng cách sử dụng từ khóa super
. Nó cũng loại bỏ sự mơ hồ giữa phương thức của lớp cha và lớp con có cùng tên phương thức và được gọi thông qua các đối tượng.
Một ví dụ về việc sử dụng từ khóa super
được hiển thị trong Đoạn mã bên dưới.
class ParentClass {
String subject = "Example of Super Keyword";
}
class SubClass extends ParentClass {
String subject = "Science";
void showMessage() {
print(super.subject);
print("Subject has ${subject.length} letters.");
}
}
void main() {
SubClass myClass = SubClass();
myClass.showMessage();
}
Trong Đoạn Mã 8, hai lớp được tạo, một là lớp cha và một là lớp con. Trong lớp cha, một biến subject
được khai báo. Lớp con được mở rộng từ lớp cha. Trong lớp con, một hàm showMessage()
được khai báo và sau đó, với sự trợ giúp của hàm print()
, các giá trị từ cả lớp cha và lớp con được in ra. Một đối tượng của lớp con được tạo trong hàm main()
và sau đó hàm showMessage()
được gọi.
Kết quả của Đoạn mã được hiển thị trong Hình bên dưới.