Cùng tìm hiểu các kiến thức về Lập trình hướng đối tượng trong Java thông qua bài toán quản lý điểm số của sinh viên đại học, trong đó giả định chương trình bao gồm 3 học phần: Lập trình C++, Lập trình Java, và Lập trình Web.

Trong lập trình hướng đối tượng, chúng ta có bốn nguyên tắc cơ bản:

  • Encapsulation (Đóng gói): Giúp ẩn chi tiết nội bộ của đối tượng và chỉ cho phép truy cập qua các phương thức. Trong bài toán này, chúng ta sẽ đóng gói thông tin sinh viên và điểm số các môn học để bảo vệ dữ liệu.
  • Inheritance (Kế thừa): Cho phép một lớp thừa hưởng thuộc tính và phương thức từ lớp khác. Ví dụ, lớp SinhVien có thể là lớp cha cho các lớp SinhVienChinhQuySinhVienLienThong, và cả hai lớp này sẽ kế thừa các thuộc tính và phương thức của lớp SinhVien.
  • Polymorphism (Đa hình): Cho phép thực hiện một hành động trên nhiều đối tượng khác nhau. Ở đây, các loại sinh viên khác nhau có thể tính điểm trung bình với cách tính khác nhau.
  • Abstraction (Trừu tượng hóa): Giúp tập trung vào các thuộc tính và hành vi quan trọng, ẩn đi những chi tiết không cần thiết. Trong bài toán này, ta sẽ dùng các lớp trừu tượng và interface để định nghĩa các hành vi mà sinh viên phải có.

2. Xây dựng lớp SinhVien trong Java

Lớp SinhVien sẽ lưu trữ thông tin của sinh viên như mã sinh viên, họ tên, và điểm của từng môn học: Lập trình C++, Lập trình Java và Lập trình Web. Chúng ta sẽ đóng gói các thuộc tính và truy cập chúng qua phương thức gettersetter.

public class SinhVien {
private String maSinhVien;
private String hoTen;
private double diemLTCpp;
private double diemLTJava;
private double diemLTWeb;

// Constructor
public SinhVien(String maSinhVien, String hoTen, double diemLTCpp, double diemLTJava, double diemLTWeb) {
this.maSinhVien = maSinhVien;
this.hoTen = hoTen;
this.diemLTCpp = diemLTCpp;
this.diemLTJava = diemLTJava;
this.diemLTWeb = diemLTWeb;
}

// Getter và Setter
public String getMaSinhVien() {
return maSinhVien;
}

public void setMaSinhVien(String maSinhVien) {
this.maSinhVien = maSinhVien;
}

public String getHoTen() {
return hoTen;
}

public void setHoTen(String hoTen) {
this.hoTen = hoTen;
}

public double getDiemLTCpp() {
return diemLTCpp;
}

public void setDiemLTCpp(double diemLTCpp) {
this.diemLTCpp = diemLTCpp;
}

public double getDiemLTJava() {
return diemLTJava;
}

public void setDiemLTJava(double diemLTJava) {
this.diemLTJava = diemLTJava;
}

public double getDiemLTWeb() {
return diemLTWeb;
}

public void setDiemLTWeb(double diemLTWeb) {
this.diemLTWeb = diemLTWeb;
}

// Phương thức tính điểm trung bình
public double tinhDiemTrungBinh() {
return (diemLTCpp + diemLTJava + diemLTWeb) / 3;
}

// Phương thức hiển thị thông tin sinh viên
public void hienThiThongTin() {
System.out.println(“Mã SV: ” + maSinhVien + “, Họ tên: ” + hoTen);
System.out.println(“Điểm trung bình: ” + tinhDiemTrungBinh());
}
}

3. Tạo đối tượng từ lớp SinhVien

Dưới đây là cách tạo một đối tượng SinhVien và hiển thị thông tin.

public class Main {
public static void main(String[] args) {
SinhVien sv1 = new SinhVien(“SV001”, “Nguyễn Văn A”, 7.5, 8.0, 9.0);
sv1.hienThiThongTin(); // Hiển thị thông tin sinh viên
}
}

4. Tính kế thừa (Inheritance)

Giả sử chúng ta muốn quản lý cả sinh viên chính quy và sinh viên liên thông. Chúng ta có thể tạo hai lớp SinhVienChinhQuySinhVienLienThong, và cả hai đều kế thừa lớp SinhVien.

public class SinhVienChinhQuy extends SinhVien {
private String maLop;

public SinhVienChinhQuy(String maSinhVien, String hoTen, double diemLTCpp, double diemLTJava, double diemLTWeb, String maLop) {
super(maSinhVien, hoTen, diemLTCpp, diemLTJava, diemLTWeb);
this.maLop = maLop;
}

// Phương thức riêng cho SinhVienChinhQuy
public String getMaLop() {
return maLop;
}

public void setMaLop(String maLop) {
this.maLop = maLop;
}

@Override
public void hienThiThongTin() {
super.hienThiThongTin();
System.out.println(“Mã lớp: ” + maLop);
}
}

5. Tính đa hình (Polymorphism)

Với tính đa hình, các lớp sinh viên khác nhau có thể tính điểm trung bình theo cách khác nhau. Ví dụ, sinh viên liên thông có thể tính điểm trung bình chỉ với một số môn cụ thể.

public class SinhVienLienThong extends SinhVien {

public SinhVienLienThong(String maSinhVien, String hoTen, double diemLTCpp, double diemLTJava, double diemLTWeb) {
super(maSinhVien, hoTen, diemLTCpp, diemLTJava, diemLTWeb);
}

// Override phương thức tính điểm trung bình
@Override
public double tinhDiemTrungBinh() {
// Giả sử sinh viên liên thông chỉ tính điểm của Lập trình C++ và Lập trình Java
return (getDiemLTCpp() + getDiemLTJava()) / 2;
}
}

7. Tính trừu tượng (Abstraction)

Chúng ta có thể tạo một interface TinhDiem để định nghĩa phương thức tinhDiemTrungBinh() mà tất cả các lớp sinh viên phải thực hiện.

public interface TinhDiem {
double tinhDiemTrungBinh();
}

public abstract class SinhVien implements TinhDiem {
// Các thuộc tính và phương thức của lớp SinhVien
}

Ví dụ về Tính Trừu Tượng với Interface

Nếu chỉ quan tâm đến hành vi tính điểm mà không cần kế thừa thuộc tính, chúng ta có thể dùng interface để định nghĩa một phương thức trừu tượng tinhDiemTrungBinh().

public interface TinhDiem {
double tinhDiemTrungBinh();
}

public class SinhVienChinhQuy implements TinhDiem {
private double diemLTCpp;
private double diemLTJava;
private double diemLTWeb;

public SinhVienChinhQuy(double diemLTCpp, double diemLTJava, double diemLTWeb) {
this.diemLTCpp = diemLTCpp;
this.diemLTJava = diemLTJava;
this.diemLTWeb = diemLTWeb;
}

@Override
public double tinhDiemTrungBinh() {
return (diemLTCpp + diemLTJava + diemLTWeb) / 3;
}
}

CÂU HỎI

1. Kể tên các nguyên tắc trong lập trình hướng đối tượng

2. Tại sao cần thiết phải sử dụng các thuộc tính private trong một lớp và truy cập chúng thông qua các phương thức gettersetter?

3. Trong Java, khi nào bạn sẽ sử dụng kế thừa giữa các lớp? Để thực hiện việc kế thừa trong Java ta dùng từ khóa nào? Cho ví dụ cụ thể (khác với VD quản lý điểm, sách nêu trên)

BÀI TẬP

Xây dựng một hệ thống quản lý thư viện sách, trong đó:

  1. Lớp cơ bản Sach:
    • Thuộc tính: maSach (mã sách), tenSach (tên sách), tacGia (tác giả), namXuatBan (năm xuất bản), giaTien (giá tiền).
    • Phương thức:
      • hienThiThongTin() để hiển thị thông tin sách.
  2. Lớp con SachThamKhao (Kế thừa từ lớp Sach):
    • Thuộc tính riêng: thue (thuế tính thêm cho sách tham khảo).
    • Phương thức: tinhGiaBan() để tính giá bán sách tham khảo, có bao gồm thuế.
  3. Lớp con SachGiaoKhoa (Kế thừa từ lớp Sach):
    • Thuộc tính riêng: tinhTrang (tình trạng sách, có thể là “mới” hoặc “cũ”).
    • Phương thức: tinhGiaBan() để tính giá bán sách giáo khoa. Nếu sách cũ, giá bán chỉ còn 50% giá gốc.
  4. Sử dụng tính Đa hình:
    • Tạo một phương thức hienThiGiaBan() có thể tính giá bán và hiển thị thông tin sách bất kể loại sách nào (SachThamKhao hoặc SachGiaoKhoa).
  5. Chương trình chính:
    • Tạo danh sách các đối tượng Sach, SachThamKhao, và SachGiaoKhoa.
    • Thực hiện tính giá bán và hiển thị thông tin cho từng loại sách bằng cách duyệt qua danh sách sách.