上一篇我們用了JAVA annotation實現meta programming,不知道那是什麼的可以看這裡
https://en.wikipedia.org/wiki/Metaprogramming
重點是,那不重要
讓我們看看User類別,很顯然的ID、password以及log都與User關聯甚低,他們應該屬於另一層次,所以我們實作帳號型別以存放他們
class Account {
TransactionLogger log = new TransactionLogger();
String ID;
String password;
Account(String ID, String password) {
this.ID = ID;
this.password = password;
}
}
Account ac;
User(String ID, String password) {
ac = new Account(ID, password);
}
注意我用的是inner class,接著我們得改一些地方才能使用新的做法,但我不會在這裡寫,因為只是改改參考方式
現在我們看到我們的帳戶沒有餘額這個欄位,所以我們要補上
然後我還新增了一個構建子做為選擇
接著讓我們將提存款流程補完整
@IfChoose(value = 1)
void deposit() {
System.out.println("請輸入存款金額:");
int amountOfDeposit = Main.sc.nextInt();
ac.balance += amountOfDeposit;
ac.log.AddDepositLog(amountOfDeposit);
}
@IfChoose(value = 2)
void withdraw() {
System.out.println("請輸入提款金額:");
int amountOfWithdraw = Main.sc.nextInt();
if (amountOfWithdraw < ac.balance) {
ac.balance -= amountOfWithdraw;
ac.log.AddWithdrawLog(amountOfWithdraw);
} else {
System.out.println("操作失敗,餘額不足");
}
}
這是他們全新的定義,我們檢查了可能的失敗好提醒使用者發生了什麼,並且拒絕交易
接著我們把副作用與使用者介面分開
ac.userDeposit(amountOfDeposit); // In deposit method
就像這樣,好讓我們可以寫測試程式,由於記錄器已經有驗證程式了,所以我們把焦點放在帳戶餘額身上
@Test
public void depositTest() {
User u = new User("", "", 50000);
u.ac.userDeposit(10000);
Assert.assertEquals(60000, u.ac.balance, 0.0000001);
}
另一個測試長得差不多
測試通過了之後呢
讓我們把焦點移到難看的帳號驗證部分
for (User u : Users) {
if (u.ac.ID.equals(userID) && u.ac.password.equals(userPassword)) {
// ...
}
}
可以看得出來,這很難看懂,所以我們動手改一改
for (User u : Users) {
if (u.ac.ID.equals(userID) && u.ac.password.equals(userPassword)) {
AccountExist = true;
which = Users.indexOf(u);
break;
}
}
這裡判斷是否存在這個帳號
if (AccountExist) {
System.out.println("請選擇功能,\n[1] 存款\n[2] 提款");
int choose = sc.nextInt();
for (Method m : Users.get(which).getClass().getDeclaredMethods()) {
IfChoose c = m.getAnnotation(IfChoose.class);
if (choose == c.value()) {
m.invoke(Users.get(which));
break;
}
}
}
操作就移到後面,這樣看來只是更複雜,所以我們必須提煉出函數來表明意圖
static boolean exist(String userID, String userPassword) {
for (User u : Users) {
if (u.ac.ID.equals(userID) && u.ac.password.equals(userPassword)) {
which = Users.indexOf(u);
return true;
}
}
return false;
}
為了這麼做,which被改為Main的static變數
這時候我們已經能看出,Users的需求已足以令我們為其準備一個抽象層了,所以我們要從Main中分離出它的部分
第一步很容易決定,Users必須擁有自己的操作類別
public class DataBase {
static List<User> Users = new ArrayList<>();
static ResultAccount find(String userID, String userPassword) {
ResultAccount answer = new ResultAccount();
for (User u : DataBase.Users) {
if (u.ac.ID.equals(userID) && u.ac.password.equals(userPassword)) {
answer.location = DataBase.Users.indexOf(u);
answer.found = true;
return answer;
}
}
answer.found = false;
return answer;
}
}
class ResultAccount {
boolean found = false;
int location = -1;
}
ResultAccount account = DataBase.find(userID, userPassword);
if (account.found) {
System.out.println("請選擇功能,\n[1] 存款\n[2] 提款");
int choose = sc.nextInt();
for (Method m : DataBase.Users.get(account.location).getClass().getDeclaredMethods()) {
IfChoose c = m.getAnnotation(IfChoose.class);
if (choose == c.value()) {
m.invoke(DataBase.Users.get(account.location));
break;
}
}
}
上面的步驟是 Users移入DataBase => exist改為find => 為了不存在的which,建立ResultAccount型別 => 改變使用方式
這下我們得以建立對Users的測試案例
public class DataBaseTest {
@Test
public void findUser() {
DataBase.Users.add(new User("1", "1", 50000));
ResultAccount an = DataBase.find("1", "1");
Assert.assertEquals(true, an.found);
Assert.assertEquals(0, an.location);
}
@Test
public void notFoundUser() {
DataBase.Users.add(new User("1", "1", 50000));
ResultAccount an = DataBase.find("2", "1");
Assert.assertEquals(false, an.found);
Assert.assertEquals(-1, an.location);
}
}
全數通過,非常不錯
這篇就到這裡,下一篇我們將實現完整的流程
NEXT: ATM 05
沒有留言:
張貼留言