Menu
CoddyTech

Triển khai ẩn dữ liệu

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

Ẩn dữ liệu không chỉ dừng lại ở việc dùng các trường private. Đó là việc thiết kế lớp sao cho các chi tiết triển khai bên trong hoàn toàn ẩn với code bên ngoài, chỉ để lộ những gì cần thiết thông qua một giao diện công khai rõ ràng.

Hãy xem một lớp theo dõi mật khẩu của người dùng. Bạn không muốn ai truy cập mật khẩu thô, kể cả chỉ để đọc:

public class UserAccount
{
    private string passwordHash;
    
    public void SetPassword(string password)
    {
        passwordHash = HashPassword(password);
    }
    
    public bool VerifyPassword(string password)
    {
        return HashPassword(password) == passwordHash;
    }
    
    private string HashPassword(string password)
    {
        return password.GetHashCode().ToString();
    }
}

Lưu ý rằng không có cách nào để lấy lại mật khẩu hoặc thậm chí là mã băm. Code bên ngoài chỉ có thể đặt mật khẩu hoặc xác minh mật khẩu, cơ chế lưu trữ bên trong hoàn toàn bị ẩn. Phương thức HashPassword cũng là phương thức riêng tư vì đây là chi tiết triển khai có thể thay đổi mà không ảnh hưởng đến cách sử dụng lớp.

Cách làm này bảo vệ lớp của bạn khỏi bị sử dụng sai mục đích và giúp bạn tự do thay đổi phần bên trong sau này. Nếu bạn quyết định dùng thuật toán băm khác, không có code bên ngoài nào bị hỏng vì không có gì phụ thuộc vào cách lưu mật khẩu.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống ví bảo mật để minh họa việc ẩn dữ liệu thực sự. Bạn sẽ tạo một ví điện tử có số dư được bảo vệ hoàn toàn khỏi truy cập trực tiếp. Code bên ngoài chỉ có thể nạp tiền, rút tiền và kiểm tra xem có thể thực hiện giao dịch hay không, chứ không bao giờ có thể xem hoặc thao tác trực tiếp với số dư thực tế.

Bạn sẽ sắp xếp code trong hai tệp:

  • Wallet.cs: Tạo lớp Wallet trong không gian tên Finance, lớp này giữ số dư được ẩn hoàn toàn. Ví cần có:
    • Một trường private để lưu số dư (số thập phân)
    • Một phương thức private FormatBalance() trả về số dư được định dạng thành chuỗi tiền tệ như "$123.45", đây là hàm trợ giúp nội bộ không nên được công khai
    • Một phương thức public Deposit(decimal amount) chỉ cộng vào số dư nếu số tiền dương, trả về true nếu thành công, nếu không thì trả về false
    • Một phương thức public Withdraw(decimal amount) chỉ trừ vào số dư nếu số tiền dương và không vượt quá số dư hiện tại, trả về true nếu thành công, nếu không thì trả về false
    • Một phương thức public CanAfford(decimal amount) trả về true nếu số dư lớn hơn hoặc bằng số tiền, nếu không thì trả về false
    • Một phương thức public GetStatement() sử dụng phương thức private FormatBalance() bên trong và trả về "Current balance: {formatted balance}"
    Lưu ý rằng không có cách nào để đọc hoặc đặt số dư trực tiếp. Bạn chỉ có thể tương tác với số dư thông qua các phương thức được kiểm soát!
  • Program.cs: Trong tệp chính, hãy tạo một Wallet và thực hiện một loạt giao dịch bằng các giá trị đầu vào. Cho thấy cách ví bảo vệ trạng thái nội bộ mà vẫn cung cấp chức năng hữu ích thông qua giao diện public.

Bạn sẽ nhận ba giá trị đầu vào:

  • Số tiền gửi ban đầu (số thập phân)
  • Số tiền muốn rút (số thập phân)
  • Số tiền cần kiểm tra khả năng chi trả (số thập phân)

In đầu ra theo định dạng này:

Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}

Ví dụ, nếu các giá trị đầu vào là 100.50, 30.25 và 80.00, đầu ra sẽ là:

Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: False

Điểm cốt yếu là code bên ngoài không bao giờ trực tiếp truy cập trường số dư. Ngay cả chuỗi số dư đã định dạng cũng được lấy thông qua một phương thức public, phương thức này sử dụng một hàm trợ giúp private bên trong. Đây là cách ẩn dữ liệu: chi tiết triển khai hoàn toàn không hiển thị, và bạn có thể tự do thay đổi cách lưu trữ hoặc định dạng số dư mà không làm hỏng bất kỳ code nào đang dùng ví của bạn!

Tự làm thử

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // Đọc các giá trị đầu vào
        decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
        decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
        decimal checkAmount = Convert.ToDecimal(Console.ReadLine());

        // TODO: Tạo một instance của Wallet
        
        // TODO: Nạp tiền và in kết quả theo định dạng: "Deposit {amount}: {True/False}"
        
        // TODO: In sao kê bằng GetStatement()
        
        // TODO: Rút tiền và in kết quả theo định dạng: "Withdraw {amount}: {True/False}"
        
        // TODO: In sao kê bằng GetStatement()
        
        // TODO: Kiểm tra số dư có đủ hay không và in kết quả theo định dạng: "Can afford {amount}: {True/False}"
    }
}
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 C# online