Menu
CoddyTech

Hệ thống ngân hàng

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

challenge icon

Thử thách

Dễ

Hãy xây dựng một Hệ thống ngân hàng để quản lý tài khoản, giao dịch và chuyển tiền giữa các tài khoản. Bạn sẽ tạo một hệ thống có tính đóng gói phù hợp để bảo vệ dữ liệu tài chính, kế thừa cho các loại tài khoản khác nhau và bước kiểm tra để đảm bảo tính toàn vẹn của dữ liệu.

Bạn sẽ tổ chức code trong sáu tệp:

  • Transaction.php: Tạo lớp Transaction để ghi lại các hoạt động tài chính. Dùng cú pháp rút gọn hàm khởi tạo cho type (chuỗi: "deposit", "withdrawal", hoặc "transfer"), amount (số thực) và timestamp (chuỗi). Tất cả thuộc tính phải là public readonly. Thêm phương thức getDescription(): string trả về "[type]: $[amount]" (định dạng amount với 2 chữ số thập phân).
  • BankAccount.php: Thêm tệp Transaction. Tạo lớp trừu tượng BankAccount với:
    • Cú pháp rút gọn hàm khởi tạo cho accountNumber (chuỗi, public readonly) và holderName (chuỗi, public readonly)
    • Thuộc tính $balance protected, khởi tạo bằng 0.0
    • Một mảng private để lưu lịch sử giao dịch
    • getBalance(): float: trả về số dư hiện tại
    • deposit(float $amount): string: cộng vào số dư, ghi lại giao dịch và trả về "Deposited: $[amount]"
    • abstract withdraw(float $amount): string: các lớp con cài đặt quy tắc rút tiền
    • addTransaction(Transaction $t): void: phương thức protected để thêm giao dịch vào lịch sử
    • getTransactionCount(): int: trả về số lượng giao dịch
  • SavingsAccount.php: Thêm tệp BankAccount. Tạo lớp SavingsAccount kế thừa BankAccount với:
    • Hằng số của lớp INTEREST_RATE có giá trị 0.02 (2%)
    • withdraw(float $amount): string: nếu amount vượt quá số dư, trả về "Insufficient funds"; nếu không, trừ tiền, ghi lại giao dịch và trả về "Withdrew: $[amount]"
    • applyInterest(): string: tính lãi trên số dư hiện tại, cộng lãi vào như một khoản gửi tiền và trả về "Interest applied: $[interest]"
  • CheckingAccount.php: Thêm tệp BankAccount. Tạo lớp CheckingAccount kế thừa BankAccount với:
    • Thuộc tính $overdraftLimit private, được thiết lập qua hàm khởi tạo (thêm tham số thứ ba có giá trị mặc định là 100.0)
    • withdraw(float $amount): string: cho phép rút tiền nếu amount không vượt quá số dư cộng hạn mức thấu chi; khi thất bại, trả về "Insufficient funds (overdraft limit: $[limit])", khi thành công thì trả về "Withdrew: $[amount]"
    • getOverdraftLimit(): float: trả về hạn mức thấu chi
  • Bank.php: Thêm cả hai tệp tài khoản. Tạo lớp Bank để quản lý tài khoản:
    • Một mảng private để lưu tài khoản, với chỉ số là số tài khoản
    • addAccount(BankAccount $account): void: thêm một tài khoản
    • findAccount(string $accountNumber): ?BankAccount: trả về tài khoản hoặc null
    • transfer(string $fromAccount, string $toAccount, float $amount): string: chuyển tiền giữa các tài khoản. Trả về "Source account not found", "Destination account not found" hoặc kết quả rút tiền nếu thao tác rút thất bại. Khi thành công, gửi tiền vào tài khoản đích và trả về "Transferred $[amount] from [from] to [to]"
  • main.php: Thêm tệp Bank. Bạn sẽ nhận hai đầu vào: dữ liệu tài khoản (JSON) và các thao tác (JSON).

    Định dạng JSON của tài khoản:

    [{"type": "savings", "number": "SAV001", "holder": "Alice", "initial": 1000}, {"type": "checking", "number": "CHK001", "holder": "Bob", "initial": 500, "overdraft": 200}]

    Định dạng JSON của các thao tác:

    [{"op": "deposit", "account": "SAV001", "amount": 200}, {"op": "withdraw", "account": "CHK001", "amount": 600}, {"op": "transfer", "from": "SAV001", "to": "CHK001", "amount": 300}, {"op": "interest", "account": "SAV001"}, {"op": "balance", "account": "SAV001"}]

    Tạo ngân hàng và tất cả tài khoản (gửi số tiền ban đầu vào các tài khoản). Xử lý từng thao tác:

    • "deposit": In kết quả của deposit()
    • "withdraw": In kết quả của withdraw()
    • "transfer": In kết quả của transfer()
    • "interest": In kết quả của applyInterest() (chỉ áp dụng cho tài khoản tiết kiệm)
    • "balance": In "[holder] balance: $[balance]" (2 chữ số thập phân)

    In mỗi kết quả trên một dòng mới. Định dạng tất cả số tiền với 2 chữ số thập phân.

Hãy nhớ rằng các giao dịch chuyển tiền phải có tính nguyên tử: nếu rút tiền thất bại thì không được gửi tiền. Thuộc tính $balance protected cho phép các lớp con sửa đổi giá trị này nhưng vẫn giữ nó ẩn với code bên ngoài, qua đó thể hiện tính đóng gói phù hợp trong bối cảnh tài chính.

Tự làm thử

<?php

require_once 'Bank.php';

// Đọc đầu vào
$accountsJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

// Phân tích cú pháp đầu vào JSON
$accounts = (array)json_decode($accountsJson, true);
$operations = (array)json_decode($operationsJson, true);

// TODO: Tạo một instance Bank

// TODO: Tạo các tài khoản từ dữ liệu tài khoản
// Với mỗi tài khoản trong $accounts:
//   - Nếu kiểu là "savings", tạo SavingsAccount
//   - Nếu kiểu là "checking", tạo CheckingAccount (kèm hạn mức thấu chi nếu có)
//   - Thêm tài khoản vào ngân hàng
//   - Nạp số tiền ban đầu

// TODO: Xử lý từng thao tác
// Với mỗi thao tác trong $operations:
//   - "deposit": In ra kết quả của deposit()
//   - "withdraw": In kết quả của withdraw()
//   - "transfer": In kết quả của transfer()
//   - "interest": In kết quả của applyInterest() (chỉ áp dụng cho tài khoản tiết kiệm)
//   - "balance": In "[holder] balance: $[balance]" (2 chữ số thập phâ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 PHP online