Java – 物件導向 – 觀念建立

初學者觀念建立


對於初學者而言, 物件導向就是以人類社會一般的觀點來進行程式設計, 所以是非常容易的一件事. 完全不需要非常高深的程式邏輯或是其他聽起來像是文言文的用詞. 就開始吧~

怎麼開始? 先從觀念建立開始, 都先不要想寫任何程式, 有了基本的觀念架構之後, 再來慢慢將程式導入.

就現在所看到瀏覽器視窗而言, 這就是一個物件Object, 如果物件是很文言文的話, 那就是一個東西吧. 在這個東西裡面, 可以看到一些按鈕, 顯示的文字訊息還有頁籤等等. 這樣的關係, 可以這麼敘述的:

瀏覽器擁有(has-a)[按鈕], [文字訊息]及[頁籤]

這是目前顯而易見, 看得到的東西(物件). 但是也存在了一些看不到的東西, 例如: 目前瀏覽器的狀態, 是否支援JavaScript等等一些特徵. 因此, 可以這麼的敘述的:

瀏覽器擁有[目前狀態], [是否支援JavaScript]的特徵

再來看看存在瀏覽器上面其他特性, 按下視窗右上角的放大, 縮小及關閉, 就會有相關的功能, 這代表的意思是:

瀏覽器擁有[放大], [縮小]及[關閉]的功能

綜合以上的白話文內容, 假設要來開發一款瀏覽器視窗, 將會有以上的項目要設計進去. 那麼要開始寫程式了嗎? 再等一下, 要開發設計這件事情, 目的就是要能執行之後, 產生一個瀏覽器的東西, 也可能產生多個瀏覽器, 就像是工廠生產出產品一樣, 而在生產之前, 就是要好好的規劃設計圖, 要想清楚之後, 才能交給生產線去產生出一個一個的產品(東西).

就從規劃設計圖開始吧……

 

開始實作


在Java中, 所有的東西(物件)都是由特定的設計圖(類別)所產生出來的. 因此, 類別(class)就是第一個文言文的用詞, 是用來規劃東西(物件)被生產出來的樣子, 這裡所謂的樣子, 不只是外觀, 還包含特徵及功能. 假設想要來設計腳踏車, 先來一些白話文的描述:

腳踏車(Bike)的設計圖: Bike.java

// 腳踏車的設計圖
public class Bike {
}

public 表示這張設計圖, 大家都可以看得到, 都可以存取, 稱之為公開的存取修飾字, 用來修飾 class.

class 就是這張設計圖, 也就是類別

Bike 是自訂的名稱, 用來識別特定的設計圖(類別),

{ …… } 大括號的部分, 就是要來規劃設計的地方, 目前就先空空的.

以上這麼簡單的設計, 就可以開始生產出Bike的東西(物件)來玩. PlayBike.java

public class PlayBike {
  // 開始玩Bike
  public static void main(String[] args) {
    // 先宣告一個 我的Bike
    Bike myBike;
    // 建構出一個 Bike 的東西, 指定為 我的Bike
    myBike = new Bike();
  }
}

因為要能夠執行, 所以與之前的方式一樣, 開發一個public static void main(String[] args){……} 的程式執行進入點.

  • 宣告方式與基本型別一樣, 只是將型別關鍵字改為類別(設計圖)名稱, 然後自定一個變數名稱, 之後稱之為物件變數
  • 生產一個Bike的物件(東西), 使用 new 關鍵字, 將會產生出一個物件(Object, 東西)
  • 這段程式可以執行, 但是因為沒有任何輸出, 所以沒有任何感覺就執行完畢.

再來使Bike擁有以下特性:

  • 有[速度]的特性
  • 可以[加速]
  • 可以[煞車]

好, 先這樣子就夠了.

// 腳踏車的設計圖
public class Bike {
  double speed;	// 速度
  
  // 加速功能
  void upSpeed() {
    
  }
  
  // 煞車功能
  void downSpeed() {
    
  }
}
  • 速度將會是一個double型態的資料內容, 所以用double來宣告資料的型態, 其變數名稱為speed.
  • 而[加速]及[煞車]則是一個Bike所具備的功能
  • 一台腳踏車如何才能加速, 就是要透過踩踏踏板才能使其加速, 每用力踩踏一下, 就將會提升一些速度; 而如何使腳踏車減速, 當然就是透過拉一下煞車線. 這應該是常識, 物件導向的觀念就是用一般的常識來寫程式.
  • 功能的部分, 則是會以method(方法)來進行設計定義, 格式如下:
傳回值型態 自訂功能方法名稱(傳遞參數資料) {
// 實際的功能設計程式碼
}

 

接著將兩大功能分別設計上去: Bike.java

// 腳踏車的設計圖
public class Bike {
  double speed;	// 速度
  
  // 加速功能
  void upSpeed() {
    if (speed < 1) {
      speed = 1;
    }else {
      speed *= 1.2;
    }
  }
  
  // 煞車功能
  void downSpeed() {
    if (speed < 1) {
      speed = 0;
    }else {
      speed *= 0.7;
    }
  }
}

 

既然Bike的設計圖(class)增加了功能上去, 再來玩Bike囉.

public class PlayBike {
  // 開始玩Bike
  public static void main(String[] args) {
    // 先宣告一個 我的Bike
    Bike myBike;
    // 建構出一個 Bike 的東西, 指定為 我的Bike
    myBike = new Bike();
    // 印出一開始的速度
    System.out.println(myBike.speed);
    // 開始加速
    myBike.upSpeed();
    // 再加速
    myBike.upSpeed();
    // 看看現在的速度
    System.out.println(myBike.speed);
  }
}

 

封裝的特性


這真的是完美的腳踏車設計圖(class 類別)嗎? 來看看發生什麼事…

public class PlayBikeV2 {
  public static void main(String[] args) {
    Bike urBike = new Bike();
    System.out.println(urBike.speed);
    urBike.speed = 100;
    System.out.println(urBike.speed);
  }
}
  • 一開始, 先很平常的建立出一個Bike的物件實體.
  • 接著就印出一開始的速度值, 當然是 0.0
  • 後來, 竟然直接對speed的值進行設定, 設定為 100!
  • 果真, 印出來就是 100.0
  • 這, …合理嗎? 腳踏車可以直接設定其速度值? 在現實的觀點來看, 完全不合理.

問題出在哪裡? 對於一般的使用者而言, 如何會知道自己腳踏車的速度? 看得到速度嗎? 看不到!

所以, 就設計Bike而言, 必須將speed的資料進行隱藏, 不是不存在, 而是外界看不到, 也就是Java文言文中的封裝. 封裝有不同等級的處理方式, 先來看看最封閉的存取修飾字 private 來修飾 speed, 將使得 speed 的資料只有在設計圖中可以呼叫使用, 在設計圖以外的地方完全看不到, 更別說可以異動修改其值.

// 腳踏車的設計圖
public class Bike {
  private double speed;	// 速度
  
  // 加速功能
  void upSpeed() {
    if (speed < 1) {
      speed = 1;
    }else {
      speed *= 1.2;
    }
  }
  
  // 煞車功能
  void downSpeed() {
    if (speed < 1) {
      speed = 0;
    }else {
      speed *= 0.7;
    }
  }
}

一旦這樣小小的處理下去, 原本在使用Bike物件的PlayBike.java開始出現錯誤訊息了…

在上圖的第11, 17列, 因為直接呼叫顯示speed的地方出現錯誤, 因為speed已經被封裝了. 那麼應該如何才能得知其speed的數值呢? 此時再回頭想想現實中的腳踏車, 該如何得知當前的speed資料呢? 當然是去腳踏車店加裝一個速度表, 對於Bike類別而言, 就是再設計一個功能, 可以告知當時的速度, 如下:

// 腳踏車的設計圖
public class Bike {
  private double speed;	// 速度
  
  // 加速功能
  void upSpeed() {
    if (speed < 1) {
      speed = 1;
    }else {
      speed *= 1.2;
    }
  }
  
  // 煞車功能
  void downSpeed() {
    if (speed < 1) {
      speed = 0;
    }else {
      speed *= 0.7;
    }
  }
  
  // 加裝速度表
  double getSpeed() {
    return speed;
  }
}

 

再度回到使用腳踏車的程式, 就可以運用這個新功能了.

public class PlayBike {
  // 開始玩Bike
  public static void main(String[] args) {
    // 先宣告一個 我的Bike
    Bike myBike;
    // 建構出一個 Bike 的東西, 指定為 我的Bike
    myBike = new Bike();
    // 呼叫速度表, 傳回一開始的速度
    System.out.println(myBike.getSpeed());
    // 開始加速
    myBike.upSpeed();
    // 再加速
    myBike.upSpeed();
    // 呼叫速度表, 看看現在的速度
    System.out.println(myBike.getSpeed());
  }
}

 

方法複載(Overload)


對於方法而言, 相同名稱的方法, 可以因為參數的型別及個數不同, 而提供不同的方法. 各自之間, 傳回值的型別可以不相同. 以Bike類別的加速方法來看, 原本已經提供了 void upSpeed(), 現在, 想要多增加一個可以傳遞一個 double 值, 來指定加速的參數運算, 可以增加如下例:

// 加速功能, 傳遞參數
boolean upSpeed(double d) {
  if (speed < 1) {
    speed = 1;
    return false;
  }else {
    speed *= d;
    return true;
  }
}

 

 

設計觀點


兩隻程式, 要以兩種不同的觀點來處理.

  • 設計腳踏車
  • 使用腳踏車

 

當在設計Bike的時候, 所站的角度是在設計一台腳踏車, 會有什麼樣的特性是一台腳踏車的核心資料? 以上例而言, 所在意的[速度]這個核心資料, 因此, 所提供的方法(method), 也都是在針對這項核心資料去處理. 如果是一個購物車呢? 可能其當時的購物清單才是核心資料, 將會提供新增, 刪除, 異動及查詢該份購物清單為主要的功能.

而要進行可以執行的程式, 將會運用到Bike物件的時候, 只需要清楚的知道一個Bike物件是如何產生, 以及有哪些方法可以去操作這台腳踏車, 就好像現實生活中, 拿到了使用說明書一樣, 會告訴使用者如何操作使用, 但是不需要知道任何設計腳踏車的細節.

而物件導向程式設計, 可能同時扮演兩個不同的設計角度, 但是也可能只是其中一項. 一般常見的API, 大多數是已經設計好的類別, 以供開發者運用產生出物件實體.

 

 

 

本站資源一切隨緣,
不用註冊, 不看廣告
如果對您有所助益,
歡迎功德隨喜, 金額隨意,
請點擊以下...(感謝您)

功德箱/打賞箱

%d bloggers like this: