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 Java của Coddy. Bài học 85 trên 87.

Thử thách này yêu cầu bạn xây dựng một hệ thống ngân hàng mô phỏng các hoạt động tài chính trong thế giới thực bằng cách áp dụng các nguyên tắc OOP.

Hệ thống của bạn cần xử lý các loại tài khoản khác nhau như SavingsAccount và CheckingAccount, cả hai đều kế thừa lớp cơ sở Account. Mỗi loại tài khoản có hành vi riêng: tài khoản tiết kiệm có thể sinh lãi, còn tài khoản thanh toán có thể có hạn mức thấu chi.

Hãy nghĩ cách mô hình hóa các giao dịch. Một giao diện như Transactable có thể định nghĩa các phương thức deposit() và withdraw(). Bạn có thể dùng các ngoại lệ tùy chỉnh như InsufficientFundsException để xử lý các thao tác không hợp lệ một cách phù hợp.

Lớp Bank có thể quản lý nhiều tài khoản và khách hàng, qua đó thể hiện tính kết hợp. Bạn có thể áp dụng mẫu Factory để tạo các loại tài khoản khác nhau hoặc dùng mẫu Observer để thông báo cho khách hàng khi tài khoản thay đổi.

Tập trung vào tính đóng gói để bảo vệ dữ liệu nhạy cảm như số dư và số tài khoản, đồng thời dùng tính đa hình để xử lý thống nhất các loại tài khoản khác nhau.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một hệ thống ngân hàng hoàn chỉnh để thể hiện bạn đã nắm vững các nguyên tắc OOP! Bạn sẽ tạo một hệ thống có nhiều loại tài khoản, xử lý giao dịch và quản lý ngoại lệ phù hợp, được sắp xếp trong nhiều tệp.

Bạn sẽ sắp xếp code trong bảy tệp:

  • Transactable.java: Tạo một giao diện định nghĩa quy ước cho các thao tác tài chính. Giao diện cần khai báo các phương thức deposit(double amount) và withdraw(double amount), cả hai đều trả về giá trị boolean để cho biết thao tác có thành công hay không.
  • InsufficientFundsException.java: Tạo một ngoại lệ tùy chỉnh kế thừa từ Exception. Hàm khởi tạo cần nhận số tiền rút đã thử và số dư hiện tại, rồi tạo thông báo theo định dạng Insufficient funds: attempted [amount], available [balance].
  • Account.java: Tạo một lớp cơ sở trừu tượng đại diện cho mọi tài khoản ngân hàng. Thêm các trường riêng tư cho accountNumber (String) và balance (double), một hàm khởi tạo để khởi tạo cả hai, các hàm getter phù hợp, một hàm setter protected cho số dư để các lớp con có thể thay đổi, và một phương thức trừu tượng getAccountType() trả về một String. Viết phương thức getDetails() trả về "[accountNumber] ([accountType]): $[balance]" với số dư được định dạng đến 2 chữ số thập phân.
  • SavingsAccount.java: Tạo một lớp kế thừa Account và triển khai Transactable. Thêm một trường riêng tư interestRate (double). Hàm khởi tạo nhận accountNumber, số dư ban đầu và interestRate. Triển khai getAccountType() để trả về "Savings". Với deposit(), cộng số tiền vào số dư rồi trả về true. Với withdraw(), nếu số tiền vượt quá số dư thì trả về false, nếu không thì trừ số tiền và trả về true. Thêm phương thức applyInterest() để tăng số dư thêm balance * interestRate (tỷ lệ được lưu dưới dạng phân số, vì vậy 0.05 có nghĩa là 5%).
  • CheckingAccount.java: Tạo một lớp kế thừa Account và triển khai Transactable. Thêm một trường riêng tư overdraftLimit (double). Hàm khởi tạo nhận accountNumber, số dư ban đầu và overdraftLimit. Triển khai getAccountType() để trả về "Checking". Với deposit(), cộng số tiền vào số dư rồi trả về true. Với withdraw(), cho phép rút tiền nếu số tiền nhỏ hơn hoặc bằng số dư cộng overdraftLimit, nếu vượt quá giới hạn này thì trả về false.
  • Bank.java: Tạo một lớp quản lý tài khoản bằng ArrayList. Thêm các phương thức:
    • addAccount(Account account) - thêm một tài khoản vào ngân hàng
    • findAccount(String accountNumber) - trả về Account hoặc null nếu không tìm thấy
    • transfer(String fromAccount, String toAccount, double amount) - chuyển tiền giữa các tài khoản, ném ngoại lệ InsufficientFundsException nếu tài khoản nguồn không đủ tiền. Nếu thành công, trả về "Transferred $[amount] from [fromAccount] to [toAccount]"
    • getTotalDeposits() - trả về tổng số dư của tất cả tài khoản
  • Main.java: Xây dựng hệ thống ngân hàng của bạn! Bạn sẽ nhận một chuỗi lệnh phân tách bằng dấu phẩy:
    • SAVINGS:accountNumber:balance:interestRate
    • CHECKING:accountNumber:balance:overdraftLimit
    • DEPOSIT:accountNumber:amount
    • WITHDRAW:accountNumber:amount
    • TRANSFER:fromAccount:toAccount:amount
    • INTEREST:accountNumber

    Xử lý từng lệnh và in ra:

    • SAVINGS/CHECKING: Account created: [accountNumber]
    • DEPOSIT: Deposited $[amount] to [accountNumber]
    • WITHDRAW: Withdrew $[amount] from [accountNumber] nếu thành công, hoặc Withdrawal failed: [accountNumber]
    • TRANSFER: In thông báo thành công hoặc Transfer failed: [exception message]
    • INTEREST: Interest applied to [accountNumber]

    Sau khi xử lý tất cả lệnh, in --- Bank Summary ---, rồi in thông tin chi tiết của từng tài khoản (dùng getDetails()), cuối cùng in Total deposits: $[total] với giá trị được định dạng đến 2 chữ số thập phân.

Bạn sẽ nhận một đầu vào: chuỗi lệnh phân tách bằng dấu phẩy.

Ví dụ, với đầu vào SAVINGS:SAV001:1000.00:0.05,CHECKING:CHK001:500.00:200.00,DEPOSIT:SAV001:250.00,WITHDRAW:CHK001:600.00,TRANSFER:SAV001:CHK001:100.00,INTEREST:SAV001, đầu ra sẽ là:

Account created: SAV001
Account created: CHK001
Deposited $250.00 to SAV001
Withdrew $600.00 from CHK001
Transferred $100.00 from SAV001 to CHK001
Interest applied to SAV001
--- Bank Summary ---
SAV001 (Savings): $1207.50
CHK001 (Checking): $0.00
Total deposits: $1207.50

Lưu ý rằng tài khoản thanh toán cho phép rút $600 dù chỉ có số dư $500: đó là tác dụng của hạn mức thấu chi! Tài khoản tiết kiệm nhận lãi suất 5% trên số dư sau khi chuyển tiền. Thử thách này kết hợp kế thừa, giao diện, tính đa hình, ngoại lệ tùy chỉnh và tính đóng gói trong một ứng dụng ngân hàng thực tế!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Tạo một instance của Bank
        
        // TODO: Tách đầu vào theo dấu phẩy để lấy từng lệnh
        
        // TODO: Xử lý từng lệnh:
        // - SAVINGS:accountNumber:balance:interestRate -> Tạo SavingsAccount, in ra "Account created: [accountNumber]"
        // - CHECKING:accountNumber:balance:overdraftLimit -> Tạo CheckingAccount, in ra "Account created: [accountNumber]"
        // - DEPOSIT:accountNumber:amount -> Gửi tiền và in ra "Deposited $[amount] to [accountNumber]"
        // - WITHDRAW:accountNumber:amount -> Rút tiền và in ra thông báo thành công hoặc "Withdrawal failed: [accountNumber]"
        // - TRANSFER:fromAccount:toAccount:amount -> Chuyển tiền và in ra thông báo thành công hoặc "Transfer failed: [exception message]"
        // - INTEREST:accountNumber -> Tính lãi và in ra "Interest applied to [accountNumber]"
        
        // TODO: Sau khi xử lý tất cả các lệnh, in ra "--- Bank Summary ---"
        
        // TODO: In ra thông tin chi tiết của từng tài khoản bằng getDetails()
        
        // TODO: In ra "Total deposits: $[total]" với 2 chữ số thập phân
    }
}
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 Java online