Các khái niệm OOP đóng vai trò rất quan trọng trong lập trình. Nếu không nắm được các khái niệm OOP, bạn sẽ không thể thiết kế hệ thống theo mô hình lập trình hướng đối tượng.
OOP là gì?
Mô hinhg OPP (Object-Oriented Programming) xoay quanh khái niệm đối tượng (Object). Vậy thì đối tượng là gì? Một đối tượng là một thể hiện (instance) của một lớp (Class). Nó bao gồm các thuộc tính và các hàm (chức năng). Đối tượng tương tự như những thực thể trong thế giới thực. Ví dụ, xe ô tô, ngôi nhà, hoặc máy tính xách tay của bạn đều là các đối tượng. Chúng có các thuộc tính riêng và các phương thức để thực hiện hành vi nào đó.
Vậy lớp là gì? Lớp định là bản thiết kế (blueprint) của các đối tượng. Nó mô tả các thuộc tính và chức năng mà đối tượng sẽ có. Ví dụ, Laptop là một lớp, còn chiếc laptop của bạn là một thể hiện cụ thể của lớp đó.
Các khái niệm OOP
Các khái niệm chính trong OOPS bao gồm:
- Tính trừu tượng (Abstraction)
- Tính đóng gói (Encapsulation)
- Tính đa hình (Polymorphism)
- Tính kế thừa (Inheritance)
- Tính liên kết (Association)
- Tính tổng hợp (Aggregation)
- Tính thành phần (Composition)
Chúng ta sẽ lần lượt tìm hiểu từng khái niệm OOP trên. Các ví dụ minh họa sẽ được viết bằng ngôn ngữ lập trình Java nhằm giúp bạn hiểu cách triển khai các khái niệm OOP trong Java.
1. Tính trừu tượng
Trừu tượng là khái niệm dùng để ẩn đi các chi tiết bên trong và chỉ mô tả đối tượng dưới dạng đơn giản. Ví dụ, một phương thức dùng để cộng hai số nguyên. Quá trình xử lý bên trong của phương thức được ẩn đi. Có nhiều cách để hiện thực hóa tính trừu tượng trong lập trình hướng đối tượng, chẳng hạn như thông qua tính đóng gói hoặc kế thừa.
Một chương trình Java cũng là ví dụ điển hình của tính trừu tượng: Java tự động xử lý việc chuyển đổi các câu lệnh đơn giản sang ngôn ngữ máy và che giấu toàn bộ chi tiết triển khai với người dùng bên ngoài.
2. Tính đóng gói
Đóng gói là kỹ thuật được sử dụng để triển khai tính trừu tượng trong lập trình hướng đối tượng. Đóng gói được dùng để giới hạn quyền truy cập vào các thành viên dữ liệu và phương thức của lớp. Trong lập trình hướng đối tượng, các từ khóa kiểm soát truy cập (access modifiers) được sử dụng để thực hiện việc đóng gói.
Ví dụ, trong Java, tính đóng gói được thực hiện thông qua các từ khóa private
, protected
và public
.
3. Tính đa hình
Đa hình là khái niệm mà một đối tượng có thể hành xử theo những cách khác nhau tùy theo ngữ cảnh. Có hai loại đa hình: đa hình thời điểm biên dịch (Compile-time polymorphism) ****và đa hình thời điểm chạy (Runtime polymorphism).
Đa hình thời điểm biên dịch được hiện thực thông qua phương thức nạp chồng. Ví dụ, chúng ta có thể xây dựng một lớp như sau:
public class Circle {
public void draw(){
System.out.println("Vẽ hình tròn với màu mặc định là đen và đường kính 1 cm.");
}
public void draw(int diameter){
System.out.println("Vẽ hình tròn với màu mặc định là đen và đường kính " + diameter + " cm.");
}
public void draw(int diameter, String color){
System.out.println("Vẽ hình tròn với màu " + color + " và đường kính " + diameter + " cm.");
}
}
Ở đây có nhiều phương thức draw
nhưng mỗi phương thức có hành vi khác nhau. Đây là ví dụ của nạp chồng phương thức (method overloading) vì tất cả các phương thức đều có tên giống nhau nhưng tham số khác nhau. Vì trình biên dịch sẽ xác định được phương thức cần gọi tại thời điểm biên dịch nên được gọi là đa hình thời điểm biên dịch.
Đa hình thời điểm chạy được thực hiện khi giữa các đối tượng có mối quan hệ “IS-A”. Trường hợp này còn gọi là ghi đè phương thức (method overriding), vì lớp con cần ghi đè phương thức lên lớp cha để thực hiện đa hình thời điểm chạy. Nếu chúng ta làm việc trên kiểu dữ liệu của lớp cha, thì lớp triển khai thực tế sẽ được xác định tại thời điểm chạy.
Trình biên dịch không thể biết trước phương thức nào sẽ được gọi; quyết định này chỉ được đưa ra khi chạy chương trình, vì vậy gọi là đa hình thời điểm chạy hoặc phân phối phương thức động (dynamic method dispatch).
package com.journaldev.test;
public interface Shape {
public void draw();
}
package com.journaldev.test;
public class Circle implements Shape{
@Override
public void draw(){
System.out.println("Drwaing circle");
}
}
package com.journaldev.test;
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing Square");
}
}
Shape
là lớp cơ sở và có hai lớp con là Circle
và Square
. Dưới đây là ví dụ về đa hình thời điểm chạy:
Shape sh = new Circle();
sh.draw();
Shape sh1 = getShape(); // logic của bên thứ ba để xác định hình dạng
sh1.draw();
Trong các ví dụ trên, trình biên dịch Java không thể biết lớp triển khai thực tế của Shape
sẽ được sử dụng tại thời điểm chạy, do đó đây là trường hợp đa hình thời điểm chạy.
4. Tính kế thừa
Kế thừa là khái niệm trong lập trình hướng đối tượng cho phép một đối tượng dựa trên một đối tượng khác. Đây là cơ chế tái sử dụng mã nguồn. Đối tượng được kế thừa gọi là lớp cơ sở, còn đối tượng kế thừa gọi là lớp con. Trong Java, từ khóa extends
được dùng để triển khai tính kế thừa. Dưới đây là một ví dụ đơn giản về kế thừa trong Java:
package com.journaldev.java.examples1;
class SuperClassA {
public void foo(){
System.out.println("SuperClassA");
}
}
class SubClassB extends SuperClassA {
public void bar(){
System.out.println("SubClassB");
}
}
public class Test {
public static void main(String args[]){
SubClassB a = new SubClassB();
a.foo();
a.bar();
}
}
5. Tính liên kết
Association là khái niệm trong OOP dùng để mô tả mối quan hệ giữa các đối tượng. Liên kết mô tả tính đa trị (multiplicity) giữa các đối tượng.
Ví dụ, đối tượng Teacher
và Student
. Giữa giáo viên và học sinh là mối quan hệ một – nhiều vì một giáo viên có thể dạy nhiều học sinh mà một học sinh cũng có thể học với nhiều giáo viên. Tuy nhiên, các đối tượng Teacher
và Student
vẫn độc lập với nhau.
6. Tính tổng hợp
Aggregation là một dạng đặc biệt của Association. Trong Aggregation, các đối tượng có vòng đời riêng biệt nhưng có mối quan hệ sở hữu. Khi giữa các đối tượng tồn tại mối quan hệ HAS-A và có sự sở hữu thì đó là trường hợp của kết tập.
7. Tính thành phần
Composition là một trường hợp đặc biệt hơn nữa của Aggregation. Đây là một hình thức ràng buộc chặt chẽ hơn. Khi một đối tượng trong mối quan hệ HAS-A không thể tồn tại độc lập, thì đó là Composition.
Ví dụ, một ngôi nhà “có một” căn phòng. Trong trường hợp này, phòng không thể tồn tại nếu không có nhà. Thành phần thường được xem là tốt hơn kế thừa trong nhiều trường hợp.