Skip to main content

Tính Đóng Gói trong Java 🎯

🎯 Mục tiêu: Học cách sử dụng tính đóng gói để bảo vệ dữ liệu và đảm bảo tính toàn vẹn của đối tượng trong Java.

Giới thiệu 📝

Tính đóng gói (Encapsulation) là một trong những nguyên tắc cơ bản của lập trình hướng đối tượng. Nó giúp bảo vệ dữ liệu bằng cách ẩn các chi tiết triển khai và chỉ cung cấp các phương thức truy cập an toàn.

💡 Fun Fact: Tính đóng gói giúp code của bạn an toàn hơn và dễ bảo trì hơn bằng cách kiểm soát cách dữ liệu được truy cập và sửa đổi.

1. Cú Pháp Cơ Bản ⚡

Đóng Gói Đơn Giản

public class Student {
// Thuộc tính private
private String name;
private int age;

// Phương thức public để truy cập
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
if (age >= 0 && age <= 150) { // Kiểm tra tính hợp lệ
this.age = age;
}
}
}

Đóng Gói Với Validation

public class BankAccount {
private String accountNumber;
private double balance;

public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}

2. Các Loại Đóng Gói 🔄

Đóng Gói Với Access Modifiers

public class Employee {
private String name; // Chỉ truy cập trong lớp
protected double salary; // Truy cập trong lớp và lớp con
String department; // Truy cập trong package
public int id; // Truy cập từ mọi nơi
}

Đóng Gói Với Immutable Objects

public final class ImmutablePerson {
private final String name;
private final int age;

public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}

3. Ví Dụ Thực Tế 🚀

Quản Lý Sản Phẩm

public class Product {
private String id;
private String name;
private double price;
private int quantity;
private String category;

// Constructor
public Product(String name, double price, int quantity, String category) {
this.id = generateId();
setName(name);
setPrice(price);
setQuantity(quantity);
setCategory(category);
}

// Getters và Setters với validation
public String getId() {
return id;
}

public String getName() {
return name;
}

public void setName(String name) {
if (name != null && !name.trim().isEmpty()) {
this.name = name.trim();
}
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
if (price >= 0) {
this.price = price;
}
}

public int getQuantity() {
return quantity;
}

public void setQuantity(int quantity) {
if (quantity >= 0) {
this.quantity = quantity;
}
}

public String getCategory() {
return category;
}

public void setCategory(String category) {
if (category != null && !category.trim().isEmpty()) {
this.category = category.trim();
}
}

// Phương thức tính toán
public double calculateTotalValue() {
return price * quantity;
}

public boolean isInStock() {
return quantity > 0;
}

private String generateId() {
return "P" + System.currentTimeMillis();
}
}

Quản Lý Tài Khoản Ngân Hàng

public class BankAccount {
private final String accountNumber;
private String ownerName;
private double balance;
private String accountType;
private boolean isActive;
private List<Transaction> transactionHistory;

public BankAccount(String ownerName, String accountType) {
this.accountNumber = generateAccountNumber();
setOwnerName(ownerName);
this.balance = 0.0;
setAccountType(accountType);
this.isActive = true;
this.transactionHistory = new ArrayList<>();
}

public String getAccountNumber() {
return accountNumber;
}

public String getOwnerName() {
return ownerName;
}

public void setOwnerName(String ownerName) {
if (ownerName != null && !ownerName.trim().isEmpty()) {
this.ownerName = ownerName.trim();
}
}

public double getBalance() {
return balance;
}

public String getAccountType() {
return accountType;
}

public void setAccountType(String accountType) {
if (accountType != null && !accountType.trim().isEmpty()) {
this.accountType = accountType.trim();
}
}

public boolean isActive() {
return isActive;
}

public void setActive(boolean active) {
isActive = active;
}

public List<Transaction> getTransactionHistory() {
return new ArrayList<>(transactionHistory); // Trả về bản sao để bảo vệ dữ liệu
}

public boolean deposit(double amount) {
if (amount > 0 && isActive) {
balance += amount;
transactionHistory.add(new Transaction("DEPOSIT", amount, balance));
return true;
}
return false;
}

public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance && isActive) {
balance -= amount;
transactionHistory.add(new Transaction("WITHDRAW", -amount, balance));
return true;
}
return false;
}

public boolean transfer(BankAccount recipient, double amount) {
if (withdraw(amount)) {
if (recipient.deposit(amount)) {
transactionHistory.add(new Transaction("TRANSFER_OUT", -amount, balance));
return true;
} else {
deposit(amount); // Hoàn tác nếu chuyển khoản thất bại
}
}
return false;
}

private String generateAccountNumber() {
return "ACC" + System.currentTimeMillis();
}
}

Quản Lý Đơn Hàng

public class Order {
private final String orderId;
private String customerName;
private List<OrderItem> items;
private double totalAmount;
private String status;
private Date orderDate;

public Order(String customerName) {
this.orderId = generateOrderId();
setCustomerName(customerName);
this.items = new ArrayList<>();
this.totalAmount = 0.0;
this.status = "Mới";
this.orderDate = new Date();
}

public String getOrderId() {
return orderId;
}

public String getCustomerName() {
return customerName;
}

public void setCustomerName(String customerName) {
if (customerName != null && !customerName.trim().isEmpty()) {
this.customerName = customerName.trim();
}
}

public List<OrderItem> getItems() {
return new ArrayList<>(items); // Trả về bản sao để bảo vệ dữ liệu
}

public double getTotalAmount() {
return totalAmount;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
if (status != null && !status.trim().isEmpty()) {
this.status = status.trim();
}
}

public Date getOrderDate() {
return (Date) orderDate.clone(); // Trả về bản sao để bảo vệ dữ liệu
}

public void addItem(OrderItem item) {
if (item != null) {
items.add(item);
calculateTotal();
}
}

public void removeItem(OrderItem item) {
if (items.remove(item)) {
calculateTotal();
}
}

private void calculateTotal() {
totalAmount = items.stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}

private String generateOrderId() {
return "ORD" + System.currentTimeMillis();
}
}

4. Best Practices ✅

  1. Luôn Sử Dụng Private Cho Thuộc Tính

    // Không nên
    public class Student {
    public String name; // Thuộc tính public
    }

    // Nên
    public class Student {
    private String name; // Thuộc tính private
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    }
  2. Validation Trong Setters

    // Không nên
    public void setAge(int age) {
    this.age = age; // Không kiểm tra tính hợp lệ
    }

    // Nên
    public void setAge(int age) {
    if (age >= 0 && age <= 150) {
    this.age = age;
    }
    }
  3. Bảo Vệ Dữ Liệu Mutable

    // Không nên
    public List<OrderItem> getItems() {
    return items; // Trả về trực tiếp danh sách
    }

    // Nên
    public List<OrderItem> getItems() {
    return new ArrayList<>(items); // Trả về bản sao
    }

5. Lỗi Thường Gặp ⚠️

  1. Quên Validation

    // Lỗi
    public void setPrice(double price) {
    this.price = price; // Không kiểm tra giá trị âm
    }

    // Đúng
    public void setPrice(double price) {
    if (price >= 0) {
    this.price = price;
    }
    }
  2. Tiết Lộ Dữ Liệu Nhạy Cảm

    // Lỗi
    public String getPassword() {
    return password; // Trả về trực tiếp mật khẩu
    }

    // Đúng
    public boolean validatePassword(String input) {
    return password.equals(input);
    }
  3. Quên Clone Mutable Objects

    // Lỗi
    public Date getOrderDate() {
    return orderDate; // Trả về trực tiếp đối tượng Date
    }

    // Đúng
    public Date getOrderDate() {
    return (Date) orderDate.clone(); // Trả về bản sao
    }

💡 Lời khuyên: Hãy thực hành với các ví dụ thực tế để hiểu rõ hơn về cách sử dụng tính đóng gói trong các tình huống khác nhau.

Tiếp Theo 🎯

Trong các bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về tính kế thừa (Inheritance)
  • Học cách sử dụng tính đa hình (Polymorphism)
  • Thực hành với interface và abstract class
  • Tìm hiểu về package và import