Kết tập và hợp thành
Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 48 trên 87.
Cả kết tập lẫn hợp thành đều là dạng quan hệ “có”, nhưng chúng khác nhau ở một khía cạnh then chốt: quyền sở hữu và sự phụ thuộc về vòng đời.
Trong quan hệ hợp thành, đối tượng thành phần không thể tồn tại độc lập với đối tượng chứa nó. Khi đối tượng chứa bị hủy, các thành phần của nó cũng bị hủy. Hãy nghĩ đến một House và các đối tượng Room của nó: các phòng không tồn tại nếu không có ngôi nhà:
class House {
private Room[] rooms;
public House(int numRooms) {
rooms = new Room[numRooms];
for (int i = 0; i < numRooms; i++) {
rooms[i] = new Room(); // House tạo và sở hữu rooms
}
}
}Trong quan hệ kết tập, đối tượng được chứa có thể tồn tại độc lập. Đối tượng chứa sử dụng đối tượng đó nhưng không kiểm soát vòng đời của nó. Hãy xem xét một Team và các đối tượng Player của nó: người chơi vẫn tồn tại ngay cả khi không có đội:
class Team {
private List<Player> players;
public Team() {
players = new ArrayList<>();
}
public void addPlayer(Player player) {
players.add(player); // Team sử dụng những người chơi đã có
}
}Điểm khác biệt chính: trong quan hệ hợp thành, đối tượng chứa tự tạo các thành phần bên trong. Trong quan hệ kết tập, các đối tượng được truyền từ bên ngoài vào. Khi Team bị xóa, các đối tượng Player vẫn tồn tại. Khi House bị xóa, các đối tượng Room cũng biến mất.
Thử thách
DễHãy xây dựng một hệ thống khóa học đại học thể hiện cả quan hệ kết tập và hợp thành. Bạn sẽ thấy một Course sở hữu các đối tượng Lecture (hợp thành), còn đối với các đối tượng Professor, lớp này chỉ tham chiếu đến chúng vì chúng tồn tại độc lập (kết tập).
Bạn sẽ sắp xếp code trong bốn tệp:
Professor.java: Tạo một lớp đại diện cho giáo sư có thể tồn tại độc lập với mọi khóa học. Một Professor có hai trường riêng tư:name(String) vàdepartment(String). Thêm hàm khởi tạo để khởi tạo cả hai trường cùng các phương thức getter cho từng trường. Ghi đètoString()để trả về:Prof. [name] ([department])Lecture.java: Tạo một lớp đại diện cho bài giảng chỉ có ý nghĩa trong một khóa học. Một Lecture có hai trường riêng tư:topic(String) vàdurationMinutes(int). Thêm hàm khởi tạo và các phương thức getter. Ghi đètoString()để trả về:Lecture: [topic] ([durationMinutes] min)Course.java: Đây là nơi bạn thể hiện cả hai mối quan hệ! Một Course có ba trường riêng tư:title(String),professor(Professor, kết tập) vàlectures(mảng các đối tượng Lecture, hợp thành).Hàm khởi tạo nhận tiêu đề khóa học, một đối tượng Professor (được truyền từ bên ngoài, kết tập) và số lượng bài giảng. Bên trong hàm khởi tạo, hãy tạo mảng Lecture rồi điền các đối tượng Lecture mới vào mảng. Với mỗi bài giảng ở chỉ số
i, hãy tạo bài giảng với chủ đề"Topic " + (i + 1)và thời lượng45phút. Việc tạo các đối tượng này bên trong thể hiện quan hệ hợp thành.Thêm phương thức
getInfo()trả về một chuỗi String gồm nhiều dòng:Course: [title] Instructor: [professor.toString()] Lectures: [number of lectures]Đồng thời, thêm phương thức
listLectures()trả về một chuỗi String, trong đó kết quả toString() của từng bài giảng nằm trên một dòng riêng.Main.java: Kết hợp mọi thứ lại với nhau! Bạn sẽ nhận bốn đầu vào: tên giáo sư (String), khoa của giáo sư (String), tiêu đề khóa học (String) và số lượng bài giảng (int).Trước tiên, hãy tạo một Professor với tên và khoa đã cho. Giáo sư này tồn tại độc lập. Sau đó, tạo một Course, truyền vào tiêu đề, giáo sư (kết tập) và số lượng bài giảng (khóa học sẽ tự tạo các bài giảng bên trong, hợp thành).
In kết quả của
getInfo(), sau đó in một dòng trống, rồi in kết quả củalistLectures().
Bạn sẽ nhận bốn đầu vào theo thứ tự: tên giáo sư, khoa, tiêu đề khóa học và số lượng bài giảng.
Lưu ý điểm khác biệt chính: Professor được tạo bên ngoài rồi truyền vào Course (kết tập: giáo sư có thể dạy các khóa học khác hoặc tồn tại mà không cần khóa học này). Lecture được tạo bên trong hàm khởi tạo Course (hợp thành). Chúng chỉ thuộc về khóa học này và sẽ không tồn tại nếu thiếu khóa học đó.
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc các giá trị đầu vào
String professorName = scanner.nextLine();
String department = scanner.nextLine();
String courseTitle = scanner.nextLine();
int numberOfLectures = scanner.nextInt();
// TODO: Tạo đối tượng Professor (tồn tại độc lập, quan hệ kết tập)
// TODO: Tạo đối tượng Course, truyền đối tượng Professor vào (quan hệ kết tập)
// Course sẽ tự tạo các buổi học bên trong (quan hệ hợp thành)
// TODO: In ra kết quả của getInfo()
// TODO: In ra một dòng trống
// TODO: In ra kết quả của listLectures()
}
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online