2017编程提高第5节课——面向对象设计(5)
LanyuanXiaoyao's Blog ヽ(✿゚▽゚)ノ

2017编程提高第5节课——面向对象设计(5)


需求

  • 一个ATM是一台机器,包含读卡设备,显示屏,吐钞口,存钞口,键盘,打印机
  • 当机器空闲时,会显示一个欢迎消息,此时键盘和存钞口都是不活动的,直到一张银行卡被插入,读卡器会尝试读取这张卡,如果不可读,会提示用户并且弹出卡片
  • 如果卡片可读,读卡设备会读取账号,然后要求用户输入密码, 用户的输入应该显示为星号,而不是真正输入的数字。
  • 如果用户输入的密码正确,则显示主菜单; 如果不正确,再给用户两次输入机会,如果第三次依然失败,ATM就吞卡
  • 主菜单提示用户可以:1.存款 2.取款 3.转账 4.查看余额 用户可以选定交易,提供相关信息,交易完成后,返回主菜单假设:每个银行卡只对应一个储蓄账号
  • 如果选择了存款交易,ATM要求用户输入存款的金额,然后在存钞口放入钞票 如果选择了取款交易,ATM要求用户输入提取的金额 如果账户余额足够,并且ATM的现金足够,从吐钞口吐出相应的钞票。 如果选择了转账,ATM要求用户输入转入的账号,如果余额足够, 就会执行转账交易 如果选择了查询余额,ATM则显示账号的的余额
  • 所有的交易都是ATM和银行服务器合作完成的,银行保留了账户信息,必须在合适的时候向银行查询这些信息
  • 这里其实是两个系统, 一个运行在ATM上,另外一个在银行端

初步设计: 从需求中提取名词

从取款开始

当卡插入读卡设备,读卡设备会检测是否是一张可以识别的、真正的银行卡,检测完成以后,应该做什么事情?

答案:读取卡片上的账号细信息,显示一条消息,请输入您的密码
这暗含着CardReader 要调用Display去做事情

让ATM做中介

轮询

观察者模式

如何获取密码?

  • 需求: 每次击键后屏幕上要显示一个星号
    • ATM不得不从键盘对象(Keypad)获取一次单独的击键, 然后让屏幕对象(Display)显示一个星号,然后在获取下一次击键,再显示一个星号
    • ATM对象是个“中介”,现在他要协调Keypad对象和Display对象

抽象出场:SuperKeypad

让这个‘超级键盘’来协调用户的输入和显示星号之间的关系

开始验证

  • 在哪儿验证? ATM肯定不行,没有存储用户的密码, 必然要在银行做验证

  • 网络传输: ATM <-> 银行

    • 很多细节问题
    • 封装细节是关键!

BankProxy

BankProxy 封装所有的细节,对外提供一个简单的接口

三次失败吞卡?

如何创建一个交易对象

  1. Display :  请选择交易类型   D : 存款  W: 取款  T: 转账 B:查询余额
  2. Keypad :  (获取用户输入) ‘W’
  3. Display : 请输入存款金额
  4. Keypad : (获取用户输入)  ‘1000’
  5. 创建一个DepositTx 对象
  6. 发送给BankProxy执行

还是超级键盘

取款的细节问题

  • Transaction t = superKeypad.getTransaction(String account, String passwrod);
    • 假设t现在是个WithdrawTransaction
  • Transaction内部应该执行这些操作
    • 判断ATM现金中是否足够 (需要CashDispenser对象)
    • 发送给银行处理
    • 给用户吐出钞票(需要CashDispenser对象)
  • 方法1: 可以在SuperKeypad中在创建WithdrawTransaction之前判断 CashDispenser有没有足够现金
  • 方法2: 给Transaction 添加一个方法 isValid, 让各个子类去实现。

存款就尴尬了

存款的时候还需要从存款口取钱,需要一个DepositSlot对象, 这该怎么办?

稍微抽象一下

参数: 有的子类需要CashDepensier , 有的需要DepositSlot

使用Singleton

交易(Transaction)如何发送到银行端?

序列化

进一步细化

银行端

ATMProxy

Transaction

整体

概要的流程代码(无错误处理)

//银行卡已经插入
String account = cardReader.getAccount();
String password = superKeypad.getPassword();
Transaction t = superKeypad.getTransaction();
t.preProcess(this);
bankProxy.process(t);
t.postProcess(this);

再用存款验证一下

  1. 插入卡片, 读取卡片,提示输入密码
  2. 提示选择交易, 用户选择存款
  3. ATM让用户输入存款金额。
  4. 用户放入钞票
  5. 处理交易
//银行卡已经插入
String account = cardReader.getAccount();
String password = superKeypad.getPassword();
Transaction t = superKeypad.getTransaction();
t.preProcess(this);
bankProxy.process(t);
t.postProcess(this);

再用转账验证一下

  1. 插入卡片, 读取卡片,提示输入密码
  2. 提示选择交易, 用户选择转账
  3. ATM让用户输入对方账号和金额
  4. 处理交易
//银行卡已经插入
String account = cardReader.getAccount();
String password = superKeypad.getPassword();
Transaction t = superKeypad.getTransaction();
t.preProcess(this);
bankProxy.process(t);
t.postProcess(this);

再用查询验证一下

1.插入卡片, 读取卡片,提示输入密码 2.提示选择交易, 用户选择查询 3.处理交易

//银行卡已经插入
String account = cardReader.getAccount();
String password = superKeypad.getPassword();
Transaction t = superKeypad.getTransaction();
t.preProcess(this);
bankProxy.process(t);
t.postProcess(this);

打印

  1. 插入卡片, 读取卡片,提示输入密码
  2. 用户做了存款,转账,查询三个交易
  3. 然后选择打印
//银行卡已经插入

String account = cardReader.getAccount();
String password = superKeypad.getPassword();
List<Transaction> transactions = new ArrayList<>();
Transaction t = null;
while( (t = superKeypad.getTransaction()) != null){
	t.preProcess(this);
	bankProxy.process(t);
	t.postProcess(this);
	transactions.add(t);
}
printer.print(transactions);
cardReader.ejectCard();

思考题

客户: 用户插入卡以后,就需要输入密码, 然后ATM就向银行发出验证密码的请求, 这个过程需要访问网络,流量都要收费的
能不能改成这样:

  1. 输入密码的时候不做密码校验,可以进入主界面
  2. 真正交易(查询,转账,存款,取款)的时候再做密码验证?
  3. 一天内输错3次密码就把银行卡的密码冻结,必须到柜台激活

评论