Menu
CoddyTech

Che giấu thông tin

Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 34 trên 110.

Ẩn thông tin là nguyên tắc nền tảng của tính đóng gói, tức là một lớp nên ẩn cách hoạt động bên trong và chỉ để lộ những gì cần thiết. Bạn đã học các cơ chế (trường riêng tư, hàm lấy giá trị, hàm đặt giá trị), nhưng mục tiêu thực sự là thiết kế các lớp dễ sử dụng và khó dùng sai.

Hãy hình dung một lớp quản lý mật khẩu của người dùng. Logic lưu trữ nội bộ và băm mật khẩu phải được ẩn hoàn toàn:

class UserAccount {
  String _username;
  String _passwordHash;
  
  UserAccount(this._username, String password) 
      : _passwordHash = _hashPassword(password);
  
  static String _hashPassword(String password) {
    return 'hashed_$password';  // Ví dụ đơn giản hóa
  }
  
  bool verifyPassword(String password) {
    return _passwordHash == _hashPassword(password);
  }
  
  String get username => _username;
}

Code bên ngoài không bao giờ thấy mã băm mật khẩu hoặc biết cách lưu trữ mã đó. Chúng chỉ cần gọi verifyPassword(). Sự tách biệt này có nghĩa là bạn có thể thay đổi hoàn toàn thuật toán băm mà không ảnh hưởng đến code sử dụng lớp này.

Điều quan trọng là bạn hãy nhìn lớp từ bên ngoài vào.

Hãy tự hỏi: “Người sử dụng lớp này thực sự cần gì?” Chỉ hiển thị những gì họ cần, còn mọi thứ khác thì giữ riêng tư. Cách này tạo ra ranh giới rõ ràng giữa chức năng của lớp (giao diện công khai) và cách lớp thực hiện chức năng đó (phần triển khai riêng tư).

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống ví an toàn để thấy cách tính đóng gói thông tin được áp dụng. Bạn sẽ tạo một ví kỹ thuật số quản lý số dư và lịch sử giao dịch của người dùng, đồng thời giữ kín hoàn toàn mọi chi tiết nội bộ nhạy cảm với code bên ngoài.

Bạn sẽ sắp xếp code thành hai tệp:

  • wallet.dart: Tạo một lớp Wallet quản lý tiền an toàn. Ví nên công khai tên chủ sở hữu, nhưng ẩn mọi thứ khác sau một giao diện công khai rõ ràng:
    • Số dư nội bộ và nhật ký giao dịch phải ở chế độ riêng tư. Code bên ngoài không bao giờ được biết bạn lưu trữ hoặc theo dõi tiền như thế nào
    • Một phương thức riêng tư sẽ xử lý việc ghi lại giao dịch nội bộ (lưu các mục như "Deposit: $50.0" hoặc "Withdrawal: $30.0")
    • Cung cấp phương thức công khai deposit(double amount) để nạp tiền (chỉ khi số tiền dương) và ghi lại giao dịch nội bộ
    • Cung cấp phương thức công khai withdraw(double amount) để rút tiền chỉ khi số dư đủ và số tiền dương, đồng thời ghi lại giao dịch nội bộ. Trả về true nếu thành công, nếu không thì trả về false
    • Cung cấp getter công khai balance để trả về số dư hiện tại (chỉ đọc)
    • Cung cấp phương thức công khai getTransactionCount() để trả về số giao dịch đã ghi lại (không tiết lộ danh sách giao dịch thực tế)
    • Cung cấp phương thức công khai getLastTransaction() để trả về chuỗi giao dịch gần đây nhất hoặc "No transactions" nếu chưa có giao dịch nào
    • Thêm phương thức printSummary() để hiển thị trạng thái ví
  • main.dart: Nhập ví và minh họa cách tính đóng gói thông tin tạo ra một giao diện rõ ràng, an toàn:
    • Tạo ví cho chủ sở hữu "Sarah"
    • Nạp 100.0
    • Nạp 50.0
    • Thử rút 200.0 và in ra "Withdrawal of 200.0: [success/failed]" tùy theo kết quả
    • Rút 30.0 và in ra "Withdrawal of 30.0: [success/failed]"
    • Gọi printSummary()
    • In ra "Transaction count: [count]"
    • In ra "Last transaction: [transaction]"

Phương thức printSummary() sẽ in ra:

=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]

Hãy để ý cách code bên ngoài có thể tương tác với ví thông qua các phương thức rõ ràng, có mục đích mà không cần biết số dư được lưu trữ ra sao hoặc các giao dịch được theo dõi nội bộ như thế nào. Chi tiết triển khai được giữ kín hoàn toàn. Bạn có thể thay đổi cách lưu trữ giao dịch mà không ảnh hưởng đến bất kỳ code nào sử dụng ví.

Đầu ra dự kiến:

Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0

Tự làm thử

import 'wallet.dart';

void main() {
  // TODO: Tạo ví cho chủ sở hữu "Sarah"
  
  // TODO: Nạp 100.0 vào ví
  
  // TODO: Nạp 50.0 vào ví
  
  // TODO: Thử rút 200.0 và in kết quả
  // Định dạng: "Withdrawal of 200.0: [success/failed]"
  
  // TODO: Rút 30.0 và in kết quả
  // Định dạng: "Withdrawal of 30.0: [success/failed]"
  
  // TODO: Gọi printSummary()
  
  // TODO: In số lượng giao dịch
  // Định dạng: "Transaction count: [count]"
  
  // TODO: In giao dịch cuối cùng
  // Định dạng: "Last transaction: [transaction]"
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Dart online