封裝物件初始流程


假設你要寫個可以管理儲值卡的應用程式,首先得定義儲值卡會記錄哪些資料,像是儲值卡號碼、餘額、紅利點數,在Java中可使用class關鍵字進行定義:

package cc.openhome;
class CashCard {
    String number;
    int balance;
    int bonus;
}

假設你將這個類別是定義在cc.openhome套件,使用CashCard.java儲存,並編譯為CashCard.class,並將這個位元碼給朋友使用,你的朋友要建立5張儲值卡的資料:

CashCard card1 = new CashCard();
card1.number = "A001";
card1.balance = 500;
card1.bonus = 0;
       
CashCard card2 = new CashCard();
card2.number = "A002";
card2.balance = 300;
card2.bonus = 0;
       
CashCard card3 = new CashCard();
card3.number = "A003";
card3.balance = 1000;
card3.bonus = 1;  // 單次儲值1000元可獲得紅利一點
...

在這邊可以看到,如果想存取物件的資料成員,可以透過「.」運算子加上資料成員名稱。

你發現到每次他在建立儲值卡物件時,都會作相同的初始動作,也就是指定卡號、餘額與紅利點數,這個流程是重複的,更多的CashCard物件建立會帶來更多的程式碼重複,在程式中出現重複的流程,往往意謂著有改進的空間,在 定義類別 中談過,Java中可以定義建構式(Constructor)來改進這個問題:

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;
    }
}

建構式是與類別名稱同名的方法(Method),不用宣告傳回型態,在這個例子中,建構式上的numberbalancebonus參數,與類別的numberbalancebonus資料成員同名了,為了區別,在物件資料成員前加上this關鍵字,表示將numberbalancebonus參數的值,指定給這個物件的numberbalancebonus資料成員。

在你重新編譯CashCard.java為CashCard.class之後,交給你的朋友,同樣是建立五個CashCard物件,現在他只要這麼寫:

CashCard card1 = new CashCard("A001", 500, 0);
CashCard card2 = new CashCard("A002", 300, 0);
CashCard card3 = new CashCard("A003", 1000, 1);
...

比較看看,他應該會想寫這個程式片段,還是剛剛那個程式片段?那麼你封裝了什麼?你用了Java的建構式語法,實現物件初始化流程的封裝。封裝物件初始化流程有什麼好處?拿到CashCard類別的使用者,不用重複撰寫物件初始化流程,事實上,他也不用知道物件如何初始化,就算你修改了建構式的內容,重新編譯並給予位元碼檔案之後,CashCard類別的使用者也無需修改程式。

實際上,如果你的類別使用者想建立5個CashCard物件,並將資料顯示出來,可以用陣列,而無需個別宣告參考名稱。例如:

package cc.openhome;

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),
            new CashCard("A004", 2000, 2),
            new CashCard("A005", 3000, 3)
        };
        
        for(CashCard card : cards) {
            System.out.printf("(%s, %d, %d)%n",
                    card.number, card.balance, card.bonus);
        }
    }
}

執行結果如下所示:

(A001, 500, 0)
(A002, 300, 0)
(A003, 1000, 1)
(A004, 2000, 2)
(A005, 3000, 3)