Transaction Processing System
This challenge asks you to implement a simplified transaction processing system in Python. Such systems are fundamental to financial applications, e-commerce platforms, and any system requiring reliable record-keeping of operations that modify state. Your task is to design and implement a system that can process transactions, ensuring atomicity (all operations within a transaction succeed or fail together) and providing a basic audit trail.
Problem Description
You need to create a TransactionProcessor class that manages a bank account balance. The class should support the following operations:
deposit(amount): Adds the specifiedamountto the account balance.withdraw(amount): Subtracts the specifiedamountfrom the account balance.get_balance(): Returns the current account balance.add_transaction(description): Records a transaction with a givendescription. This should be called after a deposit or withdrawal to log the event.get_transaction_history(): Returns a list of transaction descriptions in the order they were added.
The system must ensure that withdrawals do not result in a negative balance. If a withdrawal attempt would lead to a negative balance, the withdrawal should be rejected, and the balance should remain unchanged. All operations (deposit, withdraw, add_transaction) should be treated as part of a single transaction. If any operation fails, the entire transaction should be rolled back, meaning any changes made by previous operations in the transaction should be undone.
Edge Cases to Consider:
- Invalid Input: Handle cases where
amountis not a positive number (e.g., negative numbers, zero, non-numeric values). Invalid amounts should result in the transaction being rejected and the balance remaining unchanged. - Concurrent Access: While this challenge doesn't require thread safety, consider how your design might be adapted for concurrent access in a real-world scenario.
- Large Amounts: Consider potential overflow issues if dealing with extremely large numbers. (This is less critical for this exercise but good to keep in mind).
Examples
Example 1:
Input:
processor = TransactionProcessor(100)
processor.deposit(50)
processor.add_transaction("Initial deposit")
processor.withdraw(20)
processor.add_transaction("Grocery purchase")
processor.get_balance()
Output: 130
Explanation: The initial balance is 100. A deposit of 50 increases the balance to 150. A withdrawal of 20 reduces the balance to 130.
Example 2:
Input:
processor = TransactionProcessor(100)
processor.withdraw(150)
processor.add_transaction("Attempted withdrawal")
processor.get_balance()
Output: 100
Explanation: The withdrawal of 150 is rejected because it would result in a negative balance. The balance remains at 100, and no transaction is recorded.
Example 3:
Input:
processor = TransactionProcessor(200)
processor.deposit(-10)
processor.add_transaction("Invalid deposit attempt")
processor.get_balance()
Output: 200
Explanation: The deposit of -10 is invalid. The balance remains at 200, and no transaction is recorded.
Constraints
amountfor deposit and withdrawal must be a positive number (float or integer).- The initial balance can be any non-negative number.
- Transaction descriptions are strings.
- The
get_transaction_history()method should return a list of strings. - The system should handle invalid input gracefully, rejecting operations that would violate constraints.
- The transaction history should be stored in the order transactions occurred.
Notes
- Consider using a list to store the transaction history.
- Think about how to handle errors and ensure atomicity. A simple approach is to track changes and revert them if an error occurs.
- Focus on clarity and readability in your code. Good variable names and comments are encouraged.
- You don't need to implement persistence (saving the balance and transaction history to a file or database). The data should be held in memory.
- Error handling should be done by rejecting the transaction and not raising exceptions.