進入到物件導向程式設計的第二階段,我們將更細緻地探討類別和物件的定義,以及如何在 PHP 中運用它們。此外,還會深入了解屬性和方法,特別是訪問控制和靜態成員的運用。
在上一篇中,類別是由 class
關鍵字後跟類別名稱來定義的。類別可以包含屬性(用於存儲數據)和方法(用於執行操作)。類別的實例化則是通過使用 new
關鍵字來創建類別的物件。
<?php
namespace Rewrite\\ExerciseObjectOriented;
/**
*
* Class Person
* @package Rewrite\\ExerciseObjectOriented
*/
class Person
{
/** @var string 公開屬性-姓名 「任何地方都能存取」 */
public string $name;
/** @var int 保護屬性-年齡 「僅限於此類別及繼承它的子類別內部存取」 */
protected int $age;
/** @var string 私有屬性-性別 「僅限於此類別內部存取」 */
private string $gender;
/** @var string 私有屬性-生日 「僅限於此類別內部存取」 */
private string $birthday;
/** @var string 私有屬性-興趣 「僅限於此類別內部存取」 */
private string $hobby;
/**
* Person constructor.
*
* @param string $name
* @param int $age
*/
public function __construct(string $name = '', int $age = 0, string $gender = '')
{
$this->name = $name;
$this->setAge( $age );
$this->gender = $gender;
}
/**
* 方法-設定年齡的方法「僅限於此類別內部存取」
*
* @param int $age
*/
private function setAge(int $age): void
{
if($age >= 0) {
$this->age = $age;
}
}
/**
* 方法-取得年齡的公開方法
*
* @return int
*/
public function getAge(): int
{
return $this->age;
}
/**
* 方法-設定生日的私有方法
*
* @param string $birthday
*/
private function setBirthday(string $birthday): void
{
$this->birthday = $birthday;
}
/**
* 方法-取得生日的公開方法
*
* @return string
*/
public function getBirthday(): string
{
return $this->birthday;
}
/**
* 方法-設定性別的公開方法
*
* @param string $gender
*/
protected function setGender(string $gender): void
{
$this->gender = $gender;
}
/**
* 方法-設定性別的公開方法
* 獲取性別的公開方法
*
* @return string
*/
public function getGender(): string
{
// 判斷性別是否為 m 或 f
if( in_array($this->gender, ['m', 'f']) ){
if ($this->gender === 'm') {
return '男性';
} else {
return '女性';
}
}
return '不透露';
}
/**
* 方法-設定興趣的公開方法
*
* @param string $hobby
*/
public function setHobby(string $hobby): void
{
$this->hobby = $hobby;
}
/**
* 方法-取得興趣的公開方法
*
* @return string
*/
protected function getHobby() {
return $this->hobby;
}
}
Public, Protected, Private,這就是PHP的存取控制(Visibility),透過這樣的設計,可以幫助我們更好地管理程式碼,避免不小心修改到不應該改的部分,同時也確保了資料的安全性和程式碼的可讀性。在 PHP 中運用這些存取控制,就能夠讓我們的程式碼更為清晰、結構化,也更加容易維護和擴展。
// 創建Person物件
$person = new Person("John", 30, 'M');
// 存取公開屬性
echo "姓名: " . $person->name . "<br>";
// 透過公開方法設置和獲取保護屬性
$person->setAge(30);
echo "年齡: " . $person->getAge() . "<br>";
// 直接存取私有屬性會導致錯誤
// echo $person->gender; // 錯誤
// 透過公開方法獲取私有屬性的值
echo "性別: " . $person->getGender() . "<br>";
<?php
namespace Rewrite\\ExerciseObjectOriented;
/**
* 員工
* Employee 繼承 Person
*
* Class Employee
* @package Rewrite\\ExerciseObjectOriented
*/
class Employee extends Person
{
/** @var string 員工職位 */
public string $position;
public function __construct($name, $age, $gender, $position)
{
parent::__construct($name, $age, $gender); // 呼叫父類別的構造函數
$this->position = $position;
}
/**
* 方法-取得年齡的公開方法<覆寫父類別的公開方法>
*
* @return string
*/
public function getAge(): int
{
return $this->age; // 可以存取保護屬性
}
/**
* 方法-取得興趣的公開方法<訪問父類別的私有屬性會照成錯誤>
*
* @return string
*/
public function getHobby()
{
// return $this->hobby; // 錯誤:無法直接訪問私有屬性
return "Hobby: " . parent::getHobby(); // 透過父類別的公開方法訪問
}
}
我們可以透過UML圖,更直覺的知道 getHobby 在父層是保護(protected),到了子層進行複寫(Override),改變了控制權,變成了公開(public),但在內部還是可以呼叫父層的getHobby()方法,這就是存取權限的變化。
存取修飾子(Visibility):子類方法的存取修飾子不能比父類方法更嚴格。例如,如果父類的方法是
public
,子類不能將覆寫的方法設置為private
或protected
。
在 PHP 中,靜態屬性和方法屬於類別本身,而不是類別的某個實例。可以通過類別名稱直接訪問靜態屬性和方法,而不需要創建類別的實例。
/**
* 靜態屬性和靜態方法示範
*
* Class Math
* @package Rewrite\\ExerciseObjectOriented
*/
class Math
{
/** @var float */
public static $pi = 3.14159; // 靜態屬性
/**
* @param int $num
* @return int
*/
public static function square($num)
{ // 靜態方法
return $num * $num;
}
}
echo Math::$pi; // 輸出 3.14159
echo Math::square(4); // 輸出 16
看到這邊,會不會有一種靜態方法看起來很方便的感覺呢?
使用靜態方法確實帶來一些方便,比如不需要實例化物件就能調用方法,這在某些情況下可以簡化代碼,減少資源消耗。但是,過度依賴靜態方法也有其缺點,這些缺點主要包括:
因此,雖然靜態方法在某些情境下很有用(例如工具類或單例模式),我們應該根據具體情況選擇是否使用靜態方法。
一般來說,如果一個方法不需要訪問或修改物件的狀態,那麼將其設計為靜態方法是合適的。但如果方法與物件的狀態緊密相關,則應該使用實例方法。這樣可以保持程式的靈活性和可維護性,並符合物件導向程式設計的原則。
透過這個階段的學習,我們能夠掌握如何在 PHP 中定義和使用類別、物件、屬性和方法,並了解訪問控制及靜態成員的概念和用法。這些都是物件導向程式設計中的核心元素。