アクセス修飾子の詳細
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 39/91。
3つのアクセス修飾子を理解したところで、それらが継承のシナリオでどのように動作するかを見ていきましょう。重要なポイントは、protected メンバは子クラスからアクセス可能になりますが、private メンバは子クラスからであっても隠されたままになるということです。
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber はエラーになります - private です!
// $this->getSerial() も失敗します - private メソッドです!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails();
出力:
Toyota - V6Carクラスは$brand(public)にアクセスし、getEngineInfo()(protected)を呼び出すことができますが、$serialNumberやgetSerial()にはアクセスできません。これらはVehicleに対してprivateだからです。この区別により、一部の内部詳細を子クラスと共有しながら、他の詳細を完全に隠したままにすることができます。
重要なポイント: 子クラスが内部データやメソッドにアクセスする必要がある場合は protected を使用し、子クラスであっても特定の詳細な実装に触れるべきではない場合は private を使用します。
チャレンジ
簡単継承におけるアクセス修飾子の振る舞いを探求するデバイス管理システムを構築しましょう。異なる可視性レベルのプロパティを持つベースクラス Device を作成し、それを Smartphone クラスで継承して、子クラスがどのメンバにアクセスでき、どのメンバにアクセスできないかを確認します。
コードは以下の3つのファイルに分けて構成します:
Device.php— データ感度のレベルが異なる電子デバイスを表すDeviceクラスを作成します。以下を含めてください:- 製造元名のための
publicプロパティ$brand - 内部識別用の
protectedプロパティ$modelNumber - 工場専用データのための
privateプロパティ$manufacturingCode
privateメソッドgetManufacturingCode()を追加します。"Model: [modelNumber]"を返すpublicメソッドgetModelInfo()を追加します。最後に、"[brand]-[manufacturingCode]"を返すpublicメソッドgetFullCode()を追加します。これは、クラス自身が自身のプライベートメンバにどのようにアクセスできるかを示しています。- 製造元名のための
Smartphone.php—Deviceを継承するSmartphoneクラスを作成します。Device ファイルをインクルードしてください。コンストラクタは brand、modelNumber、manufacturingCode、およびオペレーティングシステム名を受け取るようにします。最初の3つの値で親のコンストラクタを呼び出し、OSをプライベートプロパティに保存します。"[brand] [modelNumber] running [os]"を返すパブリックメソッドgetDeviceDetails()を追加します。パブリックな$brandやプロテクトされた$modelNumberには直接アクセスできますが、プライベートな$manufacturingCodeには直接アクセスできないことに注目してください。代わりに、継承されたパブリックメソッドgetFullCode()を使用する必要があります。main.php— Smartphone ファイルをインクルードします。ブランド名、モデル番号、製造コード、オペレーティングシステムの4つの入力を受け取ります。これらの値を使用してSmartphoneインスタンスを作成します。次の3行を出力してください:getDeviceDetails()を呼び出した結果- 継承された
getModelInfo()メソッドを呼び出した結果 - 継承された
getFullCode()メソッドを呼び出した結果
このチャレンジは重要な区別を浮き彫りにします。Smartphone クラスは Device から継承されたプロテクトプロパティ $modelNumber を自由に使用できますが、プライベートな $manufacturingCode や getManufacturingCode() は完全に隠されたままであり、Device が公開することを選択したパブリックインターフェースを通じてのみアクセス可能です。
チートシート
継承において、protected メンバは子クラスからアクセス可能ですが、private メンバは子クラスからであっても隠蔽されたままになります。
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber はエラーを引き起こします - privateです!
// $this->getSerial() も失敗します - privateメソッドです!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails(); // Toyota - V6
子クラスがアクセスできるもの:
publicプロパティとメソッドprotectedプロパティとメソッド
子クラスがアクセスできないもの:
privateプロパティとメソッド(これらは親クラス専用のままです)
ベストプラクティス: 子クラスが内部データやメソッドにアクセスする必要がある場合は protected を使用し、子クラスであっても特定の実装詳細に触れるべきでない場合は private を使用します。
自分で試してみよう
<?php
require_once 'Smartphone.php';
// 入力を読み込む
$brand = trim(fgets(STDIN));
$modelNumber = trim(fgets(STDIN));
$manufacturingCode = trim(fgets(STDIN));
$os = trim(fgets(STDIN));
// TODO: 入力値を使用してSmartphoneインスタンスを作成する
// TODO: getDeviceDetails()の結果を出力する
// TODO: 継承されたgetModelInfo()メソッドの結果を出力する
// TODO: 継承されたgetFullCode()メソッドの結果を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。