Khái niệm lớp trừu tượng là một trong những khái niệm cơ bản của Lập trình hướng đối tượng. Nó cho chúng ta khả năng mô hình hóa các khái niệm từ thế giới thực và tạo điều kiện cho việc sử dụng một trong các nguyên tắc OOP. tái sử dụng mã
Lớp trừu tượng trong Lập trình hướng đối tượng [OOP] là lớp không thể khởi tạo. Nói cách khác, bạn không thể tạo một đối tượng bằng lớp trừu tượng
Ok, nhưng tôi có thể làm gì với cái này?
Hãy cùng tìm hiểu những tình huống có thể xảy ra khi chúng ta có thể sử dụng các lớp trừu tượng
Tại sao chúng ta sử dụng một lớp trừu tượng
Các lớp trừu tượng có liên quan chặt chẽ với khái niệm kế thừa trong OOP. Nếu bạn không chắc kế thừa là gì, trước tiên bạn nên đọc bài đăng này để bạn có thể hiểu lớp trừu tượng là gì và tại sao chúng ta cần chúng trong OOP
Hãy nhớ rằng trước khi bắt đầu viết mã, trước tiên chúng ta nên thiết kế những gì chúng ta muốn viết mã
Một phần quan trọng trong thiết kế của chúng ta là xác định các lớp và mối quan hệ giữa chúng [thành phần, tập hợp, sử dụng và kế thừa]
Lúc này ta xác định được hai [hoặc nhiều] lớp kế thừa từ một lớp chung
Ví dụ: hãy tưởng tượng chúng ta đang lập mô hình cho một tình huống mà chúng ta phải tính lương cho nhân viên. Nhưng chúng ta phải mô hình hóa hai loại nhân viên. nhân viên bán thời gian và toàn thời gian
Như bạn hiểu thì công ty tính lương cho 2 loại nhân viên này khác nhau. Tuy nhiên, chúng vẫn có một số thuộc tính và chức năng chung. tên, họ, id, tiền lương, v.v.
Lương là thứ nên được tính cho mỗi nhân viên. Vì vậy, chúng ta nên có một phương thức để tính lương trong lớp cơ sở của chúng ta là Nhân viên. Bằng cách này, cả hai loại nhân viên sẽ kế thừa phương pháp này
Chú ý mặc dù nhân viên có lương nhưng ta cần biết loại nhân viên [bán thời gian hay toàn thời gian] để tính
Đây là một trường hợp điển hình về lý do tại sao chúng ta cần các lớp trừu tượng. Trong trường hợp này, phương pháp lương phải trừu tượng trong lớp nhân viên và có cách thực hiện tương ứng trong các lớp con theo loại nhân viên
Tìm bên dưới một ví dụ chi tiết hơn liên quan đến tiền lương của nhân viên
Ví dụ lớp trừu tượng trong Java
Đây là trường hợp sử dụng phổ biến nhất. Đây cũng là một trong những trường hợp sử dụng phổ biến nhất khi dạy/học kế thừa
Như bạn nên nhớ, OOP là về mô hình hóa các tình huống từ cuộc sống thực
Giả sử chúng ta có hai loại nhân viên trong một công ty nhất định. quản lý và nhân viên phục vụ. Nhân viên quản trị có mức lương được tính theo cấp bậc của vị trí của họ. Nếu vị trí là 1 thì lương là 5000 và nếu là 2 thì lương là 8000. Đối với nhân viên phục vụ ta tính lương là lương cơ bản + 6% lương cơ bản
Lưu ý rằng đây là một ví dụ đơn giản
Vì vậy, làm thế nào chúng ta có thể mô hình hóa tình huống này bằng OOP?
Lớp trừu tượng Nhân viên trong Java
Từ mô tả ở trên, chúng ta có thể xác định ba lớp. Nhân viên, “AdminEmployee” và “ServiceEmployee”
Lưu ý rằng tất cả “AdminEmployee” là Nhân viên và tất cả “ServiceEmployee” là Nhân viên. Điều này có nghĩa là cả “AdminEmployee” và “ServiceEmployee” đều kế thừa từ “Employee”. Nếu bạn không chắc chắn về cách xác định tài sản thừa kế, hãy đọc bài đăng theo liên kết này
Mục đích chính của chúng tôi là tính lương cho nhân viên. Nhưng lưu ý, cách tính lương còn tùy thuộc vào loại nhân viên [Admin hay phục vụ]
Điều này có nghĩa là trong lớp Nhân viên, chúng ta không thể tính lương vì chúng ta không biết cách làm. Chúng tôi không có tất cả thông tin mà chúng tôi cần, trong trường hợp này là loại nhân viên
Hãy xem cách triển khai sẽ như thế nào. Tôi sẽ sử dụng Java cho các ví dụ, nhưng logic đằng sau nó giống với các ngôn ngữ lập trình khác
abstract class Employee{ protected String name; public Employee[String name] { this.name = name; } abstract public float Salary[]; }
Đây là lớp nhân viên của chúng tôi. Trong ví dụ này, vì chúng ta không biết cách tính lương nên phương thức lương nên được khai báo trừu tượng. Nói cách khác, chúng ta không thể triển khai phương thức ở mức trừu tượng đó
Nếu chúng ta có một lớp với một phương thức trừu tượng, thì lớp đó sẽ trở thành trừu tượng. Trong trường hợp của Java, chúng ta phải khai báo rõ ràng lớp là trừu tượng bằng cách sử dụng từ khóa trừu tượng trước khai báo lớp [lớp trừu tượng Nhân viên]. Nó có thể khác ở các ngôn ngữ lập trình khác, nhưng trong tất cả chúng, một lớp có phương thức trừu tượng cũng là trừu tượng
Bây giờ, chúng ta có thể làm gì trong lớp trừu tượng này?
Sử dụng lớp trừu tượng Nhân viên
Cách chúng ta sử dụng các lớp trừu tượng là thông qua kế thừa
Hãy xem việc triển khai lớp “AdminEmployee”
class AdminEmployee extends Employee{ private Position position; public AdminEmployee[String name, Position position] { super[name]; this.position = position; } @Override public float Salary[] { float salary = 0; switch [this.position]{ case ONE: salary = 5000; case TWO: salary = 8000; } return salary; } }
Như bạn có thể thấy, lớp trước kế thừa từ lớp trừu tượng của chúng ta
Chú ý chúng ta thêm một thuộc tính mới vì nó là đặc điểm của loại đối tượng mà lớp này đại diện. Tiếp theo, chúng ta phải triển khai hàm tạo, như vậy chúng ta mới có cách khởi tạo các đối tượng kiểu này
Thực tế tiếp theo cần xem xét là bây giờ chúng ta biết cách tính lương cho loại nhân viên mà lớp này đại diện. nhân viên quản trị
Theo mô tả vấn đề của chúng tôi. ” Nhân viên quản trị có mức lương được tính theo cấp bậc của vị trí của họ. Nếu vị trí là 1 thì lương là 5000 và nếu là 2 thì lương là 8000″. Sử dụng mô tả đó, chúng tôi triển khai [ghi đè] phương thức “Salary” trong lớp mới
Bây giờ, lớp này không còn trừu tượng nữa và chúng ta có thể sử dụng nó để tạo các đối tượng như sau
AdminEmployee admin1 = new AdminEmployee[“John”, Vị trí. MỘT];
Lớp tiếp theo chúng ta phải triển khai là lớp “ServiceEmployee”
class ServiceEmployee extends Employee { private float basicSalary; public ServiceEmployee[String name, float basicSalary] { super[name]; this.basicSalary = basicSalary; } @Override public float Salary[] { return basicSalary + 6*basicSalary/100; } }
Trong trường hợp này, chúng tôi cũng có một thuộc tính mới theo báo cáo vấn đề. lương cơ bản
Vì vậy, chúng tôi tiến hành theo cách tương tự, chúng tôi thêm thuộc tính và triển khai hàm tạo để có thể khởi tạo các đối tượng mới bằng cách sử dụng lớp này
Tiếp theo, chúng tôi triển khai [ghi đè] phương thức “Salary” cho lớp mới này. Lưu ý rằng chúng tôi tính lương cho “Nhân viên phục vụ” theo cách khác với “Nhân viên quản trị”. Vì vậy, chúng tôi phản ánh sự khác biệt đó trong việc thực hiện phương pháp
Việc triển khai ở trên là cách sử dụng cổ điển của một lớp trừu tượng. Chúng tôi sử dụng chúng để mô hình hóa trừu tượng, có các hoạt động [phương thức] mà chúng tôi không thể thực hiện ở mức độ trừu tượng đó. Sau đó, chúng tôi đi xuống một mức độ trừu tượng khác bằng cách triển khai một lớp mới kế thừa từ lớp trừu tượng. Ở cấp độ trừu tượng mới này, chúng tôi biết cách triển khai các hoạt động và chúng tôi làm như vậy
Trong một hệ thống phân cấp trừu tượng, chúng ta có thể có bao nhiêu lớp trừu tượng tùy thích. Chúng ta có thể kế thừa từ một lớp trừu tượng và không triển khai các phương thức trừu tượng. Do đó, lớp mới của chúng ta cũng sẽ trừu tượng
Lớp trừu tượng không có phương thức trừu tượng trong Java
Trong Java, bạn có thể khai báo một lớp trừu tượng mà không cần phải có một phương thức trừu tượng
Điều này sẽ có tác dụng là bạn không thể tạo các đối tượng bằng cách sử dụng lớp đó vì nó trừu tượng
Đây là một cơ chế mà bạn có thể sử dụng nếu bạn muốn đảm bảo rằng không ai có thể tạo các thể hiện của lớp đó
Mặc dù, theo tôi, là một cơ chế không cần thiết, nhưng nó là một tùy chọn mà một số ngôn ngữ lập trình cung cấp
Vì vậy, nếu chúng ta đang mô hình hóa một tình huống mà chúng ta có thể biện minh cho việc sử dụng một lớp trừu tượng mà không có các phương thức trừu tượng, thì các ngôn ngữ lập trình sẽ cung cấp cho chúng ta một cách để triển khai mô hình đó
Lớp trừu tượng vs Giao diện
Một giao diện trong lập trình giúp thiết kế các chức năng phổ biến phải được thực hiện bởi một số lớp nhất định
Một cách không chính thức, bạn có thể nghĩ về một giao diện trong Java như một lớp không có thuộc tính và bất kỳ triển khai phương thức nào
Một ví dụ tuyệt vời để cho thấy cách chúng ta sử dụng các giao diện là giao diện Có thể so sánh trong Java [IComparable trong C#]
Giao diện này chỉ có một phương pháp
public interface Comparable { /** * Compares this object with the specified object for order. Returns a * negative integer, zero, or a positive integer as this object is less * than, equal to, or greater than the specified object **/ public int compareTo[T o]; }
Bất kỳ ngôn ngữ lập trình nào cũng biết cách so sánh các số nguyên, nhưng khi nói đến các loại đối tượng khác, chẳng hạn như sinh viên, chúng ta nên cho máy tính biết cách chúng ta so sánh hai sinh viên
Bất kỳ lớp nào triển khai giao diện So sánh, sẽ triển khai phương thức so sánh. Trong phương thức này, chúng ta sẽ xác định khi nào một đối tượng lớn hơn đối tượng khác
Vì vậy, tóm lại, chúng tôi sử dụng một lớp trừu tượng khi thiết kế/triển khai hệ thống phân cấp các lớp và trong một trong số chúng, chúng tôi không biết cách triển khai một phương thức nhất định. Mặt khác, chúng ta thường sử dụng một giao diện, khi chúng ta cần các lớp thực hiện một hành vi chung mà không cần quan tâm đến việc chúng có cùng hệ thống phân cấp hay không [một lớp kế thừa từ lớp kia] hay không
Lớp trừu tượng Triển khai một giao diện trong Java
Trong một số trường hợp, chúng ta có thể sử dụng các lớp trừu tượng và giao diện cùng nhau
Quay trở lại ví dụ về nhân viên. Hãy tưởng tượng chúng ta muốn có thể so sánh hai nhân viên và chúng ta sẽ nói một nhân viên lớn hơn nhân viên kia nếu tên đứng đầu theo thứ tự bảng chữ cái [“Ana” lớn hơn “Bob”]
Trong trường hợp đó, chúng ta nên thiết kế lớp Nhân viên như sau
abstract class Employee implements Comparable{ protected String name; public Employee[String name] { this.name = name; } abstract public float Salary[]; }
Trong ví dụ dưới đây, bạn có thể thấy rằng lớp trừu tượng Employee thực hiện giao diện Comparable
Từ thời điểm này, chúng ta phải lựa chọn. Chúng ta có thể triển khai các phương thức được định nghĩa trong giao diện Có thể so sánh trong lớp này Nhân viên hoặc chúng ta có thể triển khai chúng trong các lớp con. Dịch vụNhân viên và Quản trị viênNhân viên
Bởi vì chúng tôi muốn so sánh hai nhân viên theo tên của họ và tên đó nằm trong lớp Nhân viên, chúng tôi có tất cả những gì chúng tôi cần để triển khai phương thức trong lớp Nhân viên. Khi đó class Employee của chúng ta sẽ được implement như sau
abstract class Employee implements Comparable{ protected String name; public Employee[String name] { this.name = name; } abstract public float Salary[]; @Override public int compareTo[Employee o] { return name.compareTo[o.name]; } }
Kiểu String trong Java cũng cài đặt giao diện Comparable. Do đó, chúng ta có thể sử dụng phương thức compareTo được triển khai trong lớp String để so sánh hai chuỗi
Trong Java, đôi khi chúng ta sử dụng các giao diện để “mô hình hóa” nhiều kế thừa
Lớp trừu tượng trong Python
Tìm bên dưới triển khai python cho ví dụ trước của chúng tôi về nhân viên
from abc import ABC, abstractmethod class Employee[ABC]: def __init__[self, name]: self.name = name @abstractmethod def salary[self]: pass class ServiceEmployee[Employee]: def __init__[self, name, basic_salary]: super[].__init__[name] self.basic_salary = basic_salary def salary[self]: return self.basic_salary + 6*self.basic_salary/100; class AdminEmployee[Employee]: def __init__[self, name, position]: super[].__init__[name] self.position = position def salary[self]: if self.position == 1: return 5000 else: return 8000
Trừu tượng và các lớp trừu tượng
Các lớp trừu tượng cũng liên quan đến trừu tượng
Khi chúng ta sử dụng một lớp trừu tượng, chúng ta đang chỉ ra một mức độ trừu tượng nhất định
Trong trường hợp ví dụ về nhân viên, khi chúng ta định nghĩa lớp Nhân viên, chúng ta đang “ẩn” hoặc “không xem xét” tại thời điểm đó loại nhân viên cụ thể mà chúng ta đang mô hình hóa [đó là mức độ trừu tượng của chúng ta]
Ở mức độ trừu tượng đó, chúng tôi mô hình hóa mọi thứ chung cho tất cả nhân viên [tất cả họ đều có tên và mức lương] và chúng tôi trì hoãn các chi tiết cụ thể hơn
Khi chúng tôi định nghĩa lớp AdminEmployee, chúng tôi tinh chỉnh mức độ trừu tượng bằng cách thêm cách tính lương. Tuy nhiên, một số đặc điểm không được bao gồm ở mức độ trừu tượng đó vì chúng tôi không cần chúng để giải quyết vấn đề của mình. Một ví dụ là lợi ích của nhân viên này là gì, tuổi nghỉ hưu, hỗ trợ y tế, v.v.
Như bạn có thể thấy, các lớp trừu tượng và các lớp được triển khai đầy đủ [các lớp không trừu tượng] đều là các lớp trừu tượng. Chúng ta thường cần cả hai loại lớp trong Lập trình hướng đối tượng
Tóm lược
Các lớp trừu tượng rất chặt chẽ với việc sử dụng tính kế thừa [và tính đa hình]
Đó là một cơ chế hữu ích tạo điều kiện tái sử dụng mã và giúp bạn tạo các mô hình chính xác hơn và thể hiện tốt hơn các tình huống nhất định từ cuộc sống thực