Đóng gói
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Khoá này dạy cách tổ chức nhiều class cho dễ sửa, bắt đầu từ đóng gói. Tồn
kho của một sản phẩm bỗng thành số âm vì năm chỗ cùng gọi thẳng
product.setStock(...) và một chỗ quên kiểm tra. Đóng gói chặn việc này bằng
cách chỉ cho sửa dữ liệu qua đúng một cửa.
Khái niệm
🔒 Đóng gói (encapsulation): giấu dữ liệu bên trong class, chỉ cho thay đổi qua method của class để object luôn hợp lệ.
🚪 Access modifier: từ khoá quy định ai được dùng một thành viên của class.
| Từ khoá | Ai dùng được |
|---|---|
public |
mọi nơi |
private |
chỉ code bên trong class đó |
| (không ghi) | các class cùng package |
Thành viên không ghi gì thì dùng được ở mọi class cùng package (nhóm class đặt chung một thư mục).
Ví dụ
// Main.java
void main() {
var pen = new Product("Bút bi");
pen.restock(100);
pen.sell(30);
System.out.println(pen.getStock()); // 70
}
// Product.java
class Product {
private String name;
private int stock;
public Product(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getStock() {
return stock;
}
public void restock(int quantity) {
if (quantity <= 0) {
throw new IllegalArgumentException(
"Số lượng phải lớn hơn 0");
}
stock = stock + quantity;
}
public void sell(int quantity) {
if (quantity <= 0) {
throw new IllegalArgumentException(
"Số lượng phải lớn hơn 0");
}
if (quantity > stock) {
throw new IllegalStateException(
"Không đủ hàng");
}
stock = stock - quantity;
}
}stocklàprivatevà chỉ có getter, không có setter: bên ngoài đọc được quagetStock(), nhưng chỉ code trongProductmới gán được.namecũng chỉ có getter và được gán một lần trong constructor.- Muốn đổi tồn kho thì phải gọi
restockhoặcsell. Quy tắc "số lượng phải dương, không bán quá số đang có" chỉ nằm trong hai method này. - Viết
pen.stock = -5;trongmainthì lỗi compile:stock has private access in Product. Không nơi nào khác làmstockâm được nữa.
Thử ngay
Giữ nguyên Product.java của ví dụ, thay main trong Main.java bằng:
// Main.java
void main() {
var pen = new Product("Bút bi");
pen.restock(5);
try {
pen.sell(3);
pen.sell(10);
} catch (IllegalStateException ex) {
System.out.println(ex.getMessage());
}
System.out.println(pen.getStock());
}Đoán trước khi chạy: lệnh bán 10 cái bị chặn. Vậy stock cuối cùng là
5, 2 hay -8?
Xem kết quả
Không đủ hàng
2Là 2. Bán 3 cái thành công. Lệnh bán 10 bị sell chặn trước khi trừ, nên
stock không bao giờ âm.
Lỗi hay gặp
Viết setter công khai cho mọi field. Nơi nào cũng gán thẳng được, và quy
tắc trong sell bị bỏ qua.
// SAI — nơi khác gán thẳng, bỏ qua mọi kiểm tra
// Main.java
void main() {
var pen = new Product();
pen.setStock(-5);
}
// Product.java
class Product {
private int stock;
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}// ĐÚNG — bỏ setter, chỉ đổi qua method của class
pen.restock(5);Tóm tắt
- Đóng gói: dữ liệu để
private, chỉ đổi qua method của class. publicdùng được ở mọi nơi,privatechỉ dùng được trong class.- Field chỉ có getter, không có setter: bên ngoài đọc được mà không ghi được.
- Quy tắc kiểm tra nằm trong method của class, không rải ở nơi gọi.
Tự kiểm tra
0/3 câuClass Account (file Account.java) có field private long balance. Dòng account.balance = 1000; trong main thì sao?
Quy tắc "giảm giá không quá 50%" nên đặt ở đâu?
Một field khai báo int count; mà không ghi access modifier. Ai dùng được nó?