封裝物件操作流程


假設現在你的朋友使用CashCard建立3個物件,並要再對所有物件進行儲值的動作:

Scanner scanner = new Scanner(System.in);
CashCard card1 = new CashCard("A001", 500, 0);
int money = scanner.nextInt();
if(money > 0) {
    card1.balance += money;
    if(money >= 1000) {
        card1.bonus++;
    }
}
else {
    System.out.println("儲值是負的?你是來亂的嗎?");
}
       
CashCard card2 = new CashCard("A002", 300, 0);
money = scanner.nextInt();
if(money > 0) {
    card2.balance += money;
    if(money >= 1000) {
        card2.bonus++;
    }
}
else {
    System.out.println("儲值是負的?你是來亂的嗎?");
}
       
CashCard card3 = new CashCard("A003", 1000, 1);
// 還是那些if..else的重複流程
...

你的朋友作了簡單的檢查,就是儲值不能是負的,而儲值大於1000的話,就給予紅利一點,很容易就可以發現,那些儲值的流程重複了。你想了一下,儲值這個動作應該是CashCard物件自己處理!在Java中,你可以定義方法(Method)來解決這個問題:

package cc.openhome;
class CashCard {
    String number;
    int balance;
    int bonus;
    CashCard(String number, int balance, int bonus) {
        this.number = number;
        this.balance = balance;
        this.bonus = bonus;
    }
    
    void store(int money) {  // 儲值時呼叫的方法
        if(money > 0) {
            this.balance += money;
            if(money >= 1000) { 
                this.bonus++;
            }
        }
        else {
            System.out.println("儲值是負的?你是來亂的嗎?");
        }
    }
    
    void charge(int money) { // 扣款時呼叫的方法
        if(money > 0) {
            if(money <= this.balance) {
                this.balance -= money;
            }
            else {
                System.out.println("錢不夠啦!");
            }
        }
        else {
            System.out.println("扣負數?這不是叫我儲值嗎?");
        }
    }
    
    int exchange(int bonus) {  // 兌換紅利點數時呼叫的方法
        if(bonus > 0) {
            this.bonus -= bonus;
        }
        return this.bonus;
    }
}

CashCard類別中,除了定義儲值用的store()方法之外,你還考慮到扣款用的charge()方法,以及兌換紅利點數的exchange()方法。在類別中定義方法,如果不用傳回值,方法名稱前可以宣告void

先前看到的儲值重複流程,現在都封裝到store()方法中,這麼作的好處是使用CashCard的使用者,現在可以這麼撰寫了:

Scanner scanner = new Scanner(System.in);
CashCard card1 = new CashCard("A001", 500, 0);
card1.store(scanner.nextInt());
       
CashCard card2 = new CashCard("A002", 300, 0);
card2.store(scanner.nextInt());
       
CashCard card3 = new CashCard("A003", 1000, 1);
card3.store(scanner.nextInt());

好處是什麼顯而易見,相較於先前得撰寫重複流程,CashCard使用者應該會比較想寫這個吧!你封裝了什麼呢?你封裝了儲值的流程。哪天你也許考慮每加值1000元就增加一點紅利,而不像現在就算加值5000元也只有一點紅利,就算改變了store()的流程,CashCard使用者也無需修改程式。

同樣地,charge()exchange()方法也分別封裝了扣款以及兌換紅利點數的流程。為了知道兌換紅利點數後,剩餘的點數還有多少,exchange()必須傳回剩餘的點數值,方法若會傳回值,必須於方法前宣告傳回值的型態。

在Java命名慣例中,方法名稱首字是小寫。

其實如果是直接建立三個CashCard物件,而後進行儲值並顯示明細,可以如下使用陣列,讓程式更簡潔:

package cc.openhome;

import java.util.Scanner;

public class CardApp {
    public static void main(String[] args) {
        CashCard[] cards = {
            new CashCard("A001", 500, 0),
            new CashCard("A002", 300, 0),
            new CashCard("A003", 1000, 1)
        };

        Scanner scanner = new Scanner(System.in);
        for(CashCard card : cards) {
            System.out.printf("為 (%s, %d, %d) 儲值:", 
                    card.number, card.balance, card.bonus);
            card.store(scanner.nextInt());
            System.out.printf("明細 (%s, %d, %d)%n",
                    card.number, card.balance, card.bonus);
        }
    }
}

執行結果如下所示:

為 (A001, 500, 0) 儲值:1000
明細 (A001, 1500, 1)
為 (A002, 300, 0) 儲值:2000
明細 (A002, 2300, 1)
為 (A003, 1000, 1) 儲值:3000
明細 (A003, 4000, 2)