首頁 > 軟體

C#實現模擬ATM自動取款機功能

2022-08-27 22:05:26

本篇用C#實現ATM自動取款機的一些功能。面臨的第一個問題是:如何把與自動取款機相關的有形的、無形的方面抽象出來。

(1)關於使用者帳號的類:Account

該類包含與卡號、密碼、可用餘額、總餘額相關的欄位和屬性,比提供了存款和取款的方法。

namespace MyATM
{
    /// <summary>
    /// 使用者帳號
    /// </summary>
    public class Account
    {
        private int accountNumber; //卡號
        private int pin;//用來驗證
        private decimal availableBalance;//可用餘額
        private decimal totalBalance;//總餘額
        public Account(int theAccountNumber, int thePIN, decimal theAvailableBalance, decimal theTotalBalance)
        {
            accountNumber = theAccountNumber;
            pin = thePIN;
            availableBalance = theAvailableBalance;
            totalBalance = theTotalBalance;
        }
        //卡號 唯讀屬性
        public int AccountNumber
        {
            get { return accountNumber; }
        }
        //可提取餘額 唯讀屬性
        public decimal AvailableBalance
        {
            get { return availableBalance; }
        }
        //總餘額 唯讀屬性
        public decimal TotalBalance
        {
            get { return totalBalance; }
        }
        //驗證輸入密碼是否正確
        public bool ValidatePIN(int userPIN)
        {
            return (userPIN == pin);
        }
        //存款
        public void Credit(decimal amount)
        {
            totalBalance += amount;
        }
        //取款
        public void Debit(decimal amount)
        {
            availableBalance -= amount;
            totalBalance -= amount;
        }
    }
}

(2)關於銀行資料庫的類:BankDatabase

該類維護著一個Account型別的陣列,並提供驗證使用者,查詢餘額,存款、取款等方法。

namespace MyATM
{
    /// <summary>
    /// 銀行資料庫
    /// </summary>
    public class BankDatabase
    {
        private Account[] accounts;
        public BankDatabase()
        {
            accounts = new Account[2];
            accounts[0] = new Account(12345,54321,1000.00M, 1200.00M);
            accounts[1] = new Account(98765, 56789, 200.00M, 200.00M);
        }
        //根據使用者銀行卡號獲取該使用者帳號
        private Account GetAccount(int accountNumber)
        {
            foreach (Account currentAccount in accounts)
            {
                if (currentAccount.AccountNumber == accountNumber)
                {
                    return currentAccount;
                }
            }
            return null;
        }
        //驗證使用者,根據卡號和密碼
        public bool AuthenticateUser(int userAccountNumber, int userPIN)
        {
            //先根據卡號獲取帳號
            Account userAccount = GetAccount(userAccountNumber);
            if (userAccount != null)
            {
                return userAccount.ValidatePIN(userPIN);
            }
            else
            {
                return false;
            }
        }
        //返回可提取的餘額,根據卡號
        public decimal GetAvailableBalance(int userAccountNumber)
        {
            Account userAccount = GetAccount(userAccountNumber);
            return userAccount.AvailableBalance;
        }
        //返回所有餘額
        public decimal GetTotalBalance(int userAccountNumber)
        {
            Account userAccount = GetAccount(userAccountNumber);
            return userAccount.TotalBalance;
        }
        //給使用者存款
        public void Credit(int userAccountNumber, decimal amount)
        {
            Account userAccount = GetAccount(userAccountNumber);
            userAccount.Credit(amount);
        }
        //給使用者取款
        public void Debit(int userAccountNumber, decimal amount)
        {
            Account userAccount = GetAccount(userAccountNumber);
            userAccount.Debit(amount);
        }
    }
}

(3)關於ATM螢幕顯示的類:Screen

該類提供了分行顯示、不分行顯示、顯示金額這3個方法。

using System;
namespace MyATM
{
    /// <summary>
    /// 螢幕
    /// </summary>
    public class Screen
    {
        //顯示不分行的資訊
        public void DisplayMessage(string message)
        {
            Console.Write(message);
        }
        //顯示分行的資訊
        public void DisplayMessageLine(string message)
        {
            Console.WriteLine(message);
        }
        //顯示金額
        public void DisplayDollarAmmount(decimal amount)
        {
            Console.Write("{0:c}", amount);
        }
    }
}

(4)關於ATM鍵盤的類:Keypad

該類的職責很明確,就是把輸入的數位返回。

using System;
namespace MyATM
{
    /// <summary>
    /// 輸入鍵盤
    /// </summary>
    public class Keypad
    {
        //根據使用者輸入,返回一個整型
        public int GetInput()
        {
            return Convert.ToInt32(Console.ReadLine());
        }
    }
}

(5)關於進鈔、出鈔口的類:DepositSlot

該類主要是確認進鈔、出鈔口是否收到錢,預設返回true。

namespace MyATM
{
    /// <summary>
    /// 存款槽
    /// </summary>
    public class DepositSlot
    {
        //判斷是否收到錢
        public bool IsMoneyReceived()
        {
            return true;
        }
    }
}

(6)關於ATM出錢的類:CashDispendser

就像在現實生活中,ATM中肯定會預先存放一些人民幣,出錢的時候首先要判斷餘額是否足夠,如果足夠就把ATM中當前的票數做適當的減法。

namespace MyATM
{
    /// <summary>
    /// ATM取款
    /// </summary>
    public class CashDispendser
    {
        private const int INITIAL_COUNT = 500;//初始票數
        private int billCount;//當前取款機內票數
        public CashDispendser()
        {
            billCount = INITIAL_COUNT;
        }
        //出錢
        public void DispenseCash(decimal amount)
        {
            int billsRequired = ((int)amount) / 20;
            billCount -= billsRequired;
        }
        //判斷是否有餘額
        public bool IsSufficientCashAvailable(decimal amount)
        {
            //假設取款機內鈔票的面值是20
            int billsRequired = ((int) amount)/20;
            return (billCount >= billsRequired);
        }
    }
}

(7)關於事務的基礎類別:Transaction

我們可以回想一下,現實生活中,ATM的主要功能包括:查詢餘額,取款,存款等。雖然執行的過程不盡相同,但所有的這些事務包含相同的部分:比如說,必須有螢幕必須針對卡號一定和資料庫打交道,等等。於是,我們先抽象出一個有關事務的基礎類別,這個基礎類別是不需要被範例化的,所以把它定義成抽象類。如下:

namespace MyATM
{
    /// <summary>
    /// ATM事務
    /// </summary>
    public abstract class Transaction
    {
        private int accountNumber;//卡號
        private Screen userScreen;//螢幕
        private BankDatabase database;//銀行資料庫
        public Transaction(int userAccount, Screen theScreen, BankDatabase theDatabase)
        {
            accountNumber = userAccount;
            userScreen = theScreen;
            database = theDatabase;
        }
        //銀行卡號屬性 唯讀
        public int AccountNumber
        {
            get { return accountNumber; }
        }
        //使用者使用的螢幕屬性 唯讀
        public Screen UserScreen
        {
            get { return userScreen; }
        }
        //使用者使用的資料庫 唯讀
        public BankDatabase Database
        {
            get { return database; }
        }
        //抽象方法 子類必須重寫
        public abstract void Execute();
    }
}

以上,在其它有關事務的派生類中都可以存取到基礎類別的唯讀屬性,並且子類必須重寫抽象基礎類別的Execute方法。

(8)關於查詢的事務類:BalanceInquiry

該類呼叫Database類的方法查詢可用餘額和總餘額。

namespace MyATM
{
    /// <summary>
    /// ATM餘額查詢事務
    /// </summary>
    public class BalanceInquiry : Transaction
    {
         public BalanceInquiry(int userAccountNumber, Screen atmScreen, BankDatabase atmBankDatabase) : base(userAccountNumber, atmScreen, atmBankDatabase){}
        public override void Execute()
        {
            //獲取可用餘額
            decimal availableBalance = Database.GetAvailableBalance(AccountNumber);
            //獲取總餘額
            decimal totalBalance = Database.GetTotalBalance(AccountNumber);
            //列印資訊
            UserScreen.DisplayMessageLine("n餘額資訊為:");
            UserScreen.DisplayMessage(" -可用餘額為:");
            UserScreen.DisplayDollarAmmount(availableBalance);
            UserScreen.DisplayMessage("n -總餘額為:");
            UserScreen.DisplayDollarAmmount(totalBalance);
            UserScreen.DisplayMessageLine("");
        }
    }
}

(9)關於取款的事務類:Withdrawl

當用戶輸入取款的金額,該類必須要做的事情是:在使用者的銀行資料庫中和ATM上做相應的減法,還必須考慮什麼時候退出迴圈,使用者是否按了取消鍵,使用者賬戶上是否有餘額,以及ATM中是否有餘額。

namespace MyATM
{
    /// <summary>
    /// ATM取款事務
    /// </summary>
    public class Withdrawl : Transaction
    {
        private decimal amount;//取款金額
        private Keypad keypad;//鍵盤
        private CashDispendser cashDispenser;//出錢
        private const int CANCELED = 6;//對應選單中的取消
        public Withdrawl(int userAccountNumber, Screen atmScreen, BankDatabase atmBankDatabase, Keypad atmKeypad,
            CashDispendser atmCashDispenser) : base(userAccountNumber, atmScreen, atmBankDatabase)
        {
            keypad = atmKeypad;
            cashDispenser = atmCashDispenser;
        }
        public override void Execute()
        {
            bool cashDispensed = false; //表示還沒出錢
            bool transactionCanceled = false; //表示不取消事務
            do
            {
                int selection = DisplayMenuOfAmounts();
                if (selection != CANCELED)//如果使用者沒有按取消
                {
                    amount = selection; //確定取款金額
                    //根據卡號獲取可用餘額
                    decimal availableBalance = Database.GetAvailableBalance(AccountNumber);
                    if (amount <= availableBalance)//如果取款金額小於可用餘額
                    {
                        if (cashDispenser.IsSufficientCashAvailable(amount))//如果ATM餘額足夠
                        {
                            Database.Debit(AccountNumber, amount);//賬戶扣款
                            cashDispenser.DispenseCash(amount);//ATM扣款
                            cashDispensed = true;//跳出迴圈
                            UserScreen.DisplayMessageLine("n您可以拿著錢離開了~~");
                        }
                        else//如果ATM餘額不夠
                        {
                            UserScreen.DisplayMessageLine("n ATM餘額不足." + "nn請提取更小的金額~~");
                        }
                    }
                    else
                    {
                        UserScreen.DisplayMessageLine("n 賬戶餘額不足." + "nn請提取更小的金額~~");
                    }
                }
                else //如果使用者按了取消,提示正在退出並跳出迴圈
                {
                    UserScreen.DisplayMessageLine("n正在取消......");
                    transactionCanceled = true;
                }
            } while ((!cashDispensed) && (!transactionCanceled));
        }
        /// <summary>
        /// 顯示提款金額
        /// </summary>
        /// <returns></returns>
        private int DisplayMenuOfAmounts()
        {
            int userChoice = 0; //預設提款金額
            int[] amounts = {0, 20, 40, 60, 100, 200};
            while (userChoice ==0)
            {
                //顯示選單
                UserScreen.DisplayMessageLine("nWithdrawal options:");
                UserScreen.DisplayMessageLine("1-20元");
                UserScreen.DisplayMessageLine("2-40元");
                UserScreen.DisplayMessageLine("3-60元");
                UserScreen.DisplayMessageLine("4-100元");
                UserScreen.DisplayMessageLine("5-200元");
                UserScreen.DisplayMessageLine("6-取消操作");
                UserScreen.DisplayMessage("n輸入數位(1-6),選擇選項:");
                int input = keypad.GetInput();
                switch (input)
                {
                    case 1: case 2: case 3: case 4: case 5:
                        userChoice = amounts[input];
                        break;
                    case CANCELED:
                        userChoice = CANCELED;
                        break;
                    default: 
                        UserScreen.DisplayMessageLine("n輸入無效數,請重試~~");
                        break;
                }
                
            }
            return userChoice;
        }
    }
}

以上,
維護的amount變數表示的是取款金額,在每次使用者輸入提款金額後為該變數賦值。

Keypad型別的變數kepad和CashDispendser型別的變數cashDispenser需要在建構函式中為其賦初值,而這2個因素是在取款時特有的,在事務的抽象基礎類別中不需要考慮這2個因素。

通過DisplayMenuOfAmounts方法,會向用戶顯示一些面值,以及對應的數位鍵,然後根據使用者按下的數位鍵返回對應的、int型別的面值。

在Execute方法中,首先回圈的2個條件是使用者沒有按取消鍵和還沒出錢的時候。然後把DisplayMenuOfAmounts方法的返回值賦值給表示取款金額的amount變數,據此判斷使用者賬戶的餘額是否足夠,判斷ATM的餘額是否足夠,最後在使用者賬戶和ATM中分別扣款。這期間,如果使用者按了取消鍵,就把表示取消事務的變數transactionCanceled設定為true以跳出迴圈,完成扣款後把表示扣款完成的變數cashDispensed設定為true以跳出迴圈。

(10)關於存款的事務類:Deposit

該類最終是使用Database屬性把使用者輸入的金額儲存到使用者賬戶上。另外需要考慮的是:使用者在存款的時候是否按了取消鍵。

namespace MyATM
{
    /// <summary>
    /// ATM存款事務
    /// </summary>
    public class Deposit : Transaction
    {
        private decimal amount;
        private Keypad keypad;
        private DepositSlot depositSlot;
        private const int CANCELED = 0;
        public Deposit(int userAccountNumber, Screen atmScreen, BankDatabase atmBankDatabase, Keypad atmKeypad,
            DepositSlot atmDepositSlot) : base(userAccountNumber, atmScreen, atmBankDatabase)
        {
            keypad = atmKeypad;
            depositSlot = atmDepositSlot;
        }
        public override void Execute()
        {
            //確定存款金額
            amount = PromptForDepositAmount();
            if (amount != CANCELED)
            {
                UserScreen.DisplayMessage("n請輸入的存款金額為" + amount);
                //確認是否收到錢
                bool isReceived = depositSlot.IsMoneyReceived();
                if (isReceived)
                {
                    UserScreen.DisplayMessageLine("n存款成功~~");
                    Database.Credit(AccountNumber, amount);//存款到賬戶
                }
                else
                {
                    UserScreen.DisplayMessageLine("n存款時發生錯誤~~");
                }
            }
            else
            {
                UserScreen.DisplayMessageLine("n正在取消交易......");
            }
        }
        /// <summary>
        /// 顯示存款金額
        /// </summary>
        /// <returns></returns>
        private decimal PromptForDepositAmount()
        {
            UserScreen.DisplayMessage("n請輸入存款金額(輸入0退出)");
            int input = keypad.GetInput();
            if (input == CANCELED)
            {
                return CANCELED;
            }
            else
            {
                return input;
            }
        }
    }
}

以上,
私有方法PromptForDepositAmount用來返回使用者輸入的金額,如果使用者按取消鍵,就返回0。

(11)關於ATM本身的類:ATM

該類主要是提供給外部一個方法用來執行。

namespace MyATM
{
    public class ATM
    {
        private bool userAuthenticated;//表示使用者是否驗證通過
        private int currentAccountNumber;//當前交易的銀行卡號
        private Screen screen;//螢幕
        private Keypad keypad;//鍵盤
        private CashDispendser cashDispendser;//出款
        private DepositSlot depositSlot;//存款
        private BankDatabase bankDatabase;//資料庫
        //選單選項列舉
        private enum MenuOption
        {
            BANLANCE_INQUIRY = 1,//餘額查詢
            WITHDRAWAL = 2,//取款
            DEPOSIT = 3,//存款
            EXIT_ATM = 4//退出
        }
        public ATM()
        {
            userAuthenticated = false;//預設驗證不通過
            currentAccountNumber = 0;//預設卡號
            screen = new Screen();//預設螢幕
            keypad = new Keypad();//預設鍵盤
            cashDispendser = new CashDispendser();//預設出款幫助類
            bankDatabase = new BankDatabase();//預設銀行資料庫
            depositSlot = new DepositSlot();//預設存款幫助類
        }
        //執行
        public void Run()
        {
            while (true)
            {
                while (!userAuthenticated)//如果使用者沒有驗證通過,就一直迴圈
                {
                    screen.DisplayMessageLine("n歡迎");
                    AuthenticateUser();
                    PerormTransactions();
                    //重新設定一些引數
                    userAuthenticated = false;
                    currentAccountNumber = 0;
                    screen.DisplayMessageLine("n謝謝,再見~~");
                }
            }
        }
        //驗證使用者
        private void AuthenticateUser()
        {
            screen.DisplayMessage("n請輸入卡號");
            int accountNumber = keypad.GetInput();
            screen.DisplayMessage("n輸入密碼");
            int pin = keypad.GetInput();
            userAuthenticated = bankDatabase.AuthenticateUser(accountNumber, pin);
            if (userAuthenticated)
            {
                currentAccountNumber = accountNumber; //儲存當前的使用者卡號
            }
            else
            {
                screen.DisplayMessageLine("無效的卡號或密碼,請重試~~");
            }
        }
        //執行交易
        private void PerormTransactions()
        {
            Transaction currenTransaction;
            bool userExited = false; //使用者還沒選擇退出
            while (!userExited)
            {
                //確定選擇的具體事務
                int mainMenuSelction = DisplayMainMenu();
                switch ((MenuOption)mainMenuSelction)
                {
                    case MenuOption.BANLANCE_INQUIRY:
                    case MenuOption.WITHDRAWAL:
                    case MenuOption.DEPOSIT:
                        currenTransaction = CreateTransaction(mainMenuSelction);
                        currenTransaction.Execute();
                        break;
                    case MenuOption.EXIT_ATM:
                        screen.DisplayMessageLine("n正在退出系統......");
                        userExited = true;//退出迴圈
                        break;
                    default:
                        screen.DisplayMessageLine("n無效選項,請重新選擇~~");
                        break;
                }
            }
        }
        //顯示選單
        private int DisplayMainMenu()
        {
            screen.DisplayMessageLine("n主選單:");
            screen.DisplayMessageLine("1-查詢餘額");
            screen.DisplayMessageLine("2-提取現金");
            screen.DisplayMessageLine("3-存款");
            screen.DisplayMessageLine("4-退出n");
            screen.DisplayMessage("請輸入選擇:");
            return keypad.GetInput();
        }
        //建立交易
        private Transaction CreateTransaction(int type)
        {
            Transaction temp = null;
            switch ((MenuOption)type)
            {
                case MenuOption.BANLANCE_INQUIRY:
                    temp = new BalanceInquiry(currentAccountNumber, screen, bankDatabase);
                    break;
                case MenuOption.WITHDRAWAL:
                    temp = new Withdrawl(currentAccountNumber, screen, bankDatabase, keypad, cashDispendser);
                    break;
                case MenuOption.DEPOSIT:
                    temp = new Deposit(currentAccountNumber, screen, bankDatabase, keypad, depositSlot);
                    break;
            }
            return temp;
        }
    }
}

以上,
向外部提供了一個Run方法,使用者端只要呼叫該實體方法就可以了。在Run方法內部又實現了對使用者的驗證和進行使用者選擇的事務。

私有方法DisplayMainMenu用來顯示主選單項,並返回使用者的選擇。

在PerormTransactions方法中,根據使用者的選擇,使用CreateTransaction(int type)方法建立具體的事務,並最終執行。並需要考慮使用者按退出按鈕的情況。

(12)執行

using System;
namespace MyATM
{
    class Program
    {
        static void Main(string[] args)
        {
            ATM theATM = new ATM();
            theATM.Run();
            Console.ReadKey();
        }
    }
}

總結:ATM案例很好地體現了物件導向的一些特點,尤其是:當我們面對一個看似複雜的案例時,首先需要一種對有形和無形事物抽象的能力,其次要儘可能地把程式碼中一些重複的部分提煉到基礎類別中去,就像本案例中有關事務的抽象基礎類別。

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對it145.com的支援。如果你想了解更多相關內容請檢視下面相關連結


IT145.com E-mail:sddin#qq.com