• C#实现ATM自动取款机


    本篇用C#实现ATM自动取款机的一些功能。面临的第一个问题是:如何把与自动取款机相关的有形的、无形的方面抽象出来。大致如下:

     

    (1)关于用户帐号的类:Account
    (2)关于银行数据库的类:BankDatabase
    (3)关于ATM屏幕显示的类:Screen
    (4)关于ATM键盘的类:Keypad
    (5)关于进钞、出钞口的类:DepositSlot
    (6)关于ATM出钱的类:CashDispendser
    (7)关于事务的基类:Transaction
    (8)关于查询的事务类:BalanceInquiry
    (9)关于取款的事务类:Withdrawl
    (10)关于存款的事务类:Deposit
    (11)关于ATM本身的类:ATM
    (12)运行

     

    (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("
    余额信息为:");
    
                UserScreen.DisplayMessage(" -可用余额为:");
    
                UserScreen.DisplayDollarAmmount(availableBalance);
    
                UserScreen.DisplayMessage("
     -总余额为:");
    
                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("
    您可以拿着钱离开了~~");
    
                            }
    
                            else//如果ATM余额不够
    
                            {
    
                                UserScreen.DisplayMessageLine("
     ATM余额不足." + "
    
    请提取更小的金额~~");
    
                            }
    
                        }
    
                        else
    
                        {
    
                            UserScreen.DisplayMessageLine("
     账户余额不足." + "
    
    请提取更小的金额~~");
    
                        }
    
                    }
    
                    else //如果用户按了取消,提示正在退出并跳出循环
    
                    {
    
                        UserScreen.DisplayMessageLine("
    正在取消......");
    
                        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("
    Withdrawal 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("
    输入数字(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("
    输入无效数,请重试~~");
    
                            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("
    请输入的存款金额为" + amount);
    
                    //确认是否收到钱
    
                    bool isReceived = depositSlot.IsMoneyReceived();
    
                    if (isReceived)
    
                    {
    
                        UserScreen.DisplayMessageLine("
    存款成功~~");
    
                        Database.Credit(AccountNumber, amount);//存款到账户
    
                    }
    
                    else
    
                    {
    
                        UserScreen.DisplayMessageLine("
    存款时发生错误~~");
    
                    }
    
                }
    
                else
    
                {
    
                    UserScreen.DisplayMessageLine("
    正在取消交易......");
    
                }
    
            }
    
            /// <summary>
    
            /// 显示存款金额
    
            /// </summary>
    
            /// <returns></returns>
    
            private decimal PromptForDepositAmount()
    
            {
    
                UserScreen.DisplayMessage("
    请输入存款金额(输入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("
    欢迎");
    
                        AuthenticateUser();
    
                        PerormTransactions();
    
                        //重新设置一些参数
    
                        userAuthenticated = false;
    
                        currentAccountNumber = 0;
    
                        screen.DisplayMessageLine("
    谢谢,再见~~");
    
                    }
    
                }
    
            }
    
            //验证用户
    
            private void AuthenticateUser()
    
            {
    
                screen.DisplayMessage("
    请输入卡号");
    
                int accountNumber = keypad.GetInput();
    
                screen.DisplayMessage("
    输入密码");
    
                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("
    正在退出系统......");
    
                            userExited = true;//退出循环
    
                            break;
    
                        default:
    
                            screen.DisplayMessageLine("
    无效选项,请重新选择~~");
    
                            break;
    
                    }
    
                }
    
            }
    
            //显示菜单
    
            private int DisplayMainMenu()
    
            {
    
                screen.DisplayMessageLine("
    主菜单:");
    
                screen.DisplayMessageLine("1-查询余额");
    
                screen.DisplayMessageLine("2-提取现金");
    
                screen.DisplayMessageLine("3-存款");
    
                screen.DisplayMessageLine("4-退出
    ");
    
                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();
    
            }
    
        }
    
    }
    

    1

    总结:ATM案例很好地体现了面向对象的一些特点,尤其是:当我们面对一个看似复杂的案例时,首先需要一种对有形和无形事物抽象的能力,其次要尽可能地把代码中一些重复的部分提炼到基类中去,就像本案例中有关事务的抽象基类。


    参考资料:
    Visual C# 2008大学教程--(第三版)   

  • 相关阅读:
    配色方案及色彩心理学(转)
    CSS中强大的EM
    Link To Sql简单
    改变html中鼠标形状
    JS实现雪花效果
    你真的已经搞懂JavaScript了吗?
    MVC特性
    初识 bower(一)
    学习前端模板引擎 jade (一)
    日志系统
  • 原文地址:https://www.cnblogs.com/darrenji/p/3915590.html
Copyright © 2020-2023  润新知