final キーワード
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 24/91。
継承やメソッドのオーバーライドは柔軟性を提供しますが、特定のクラスやメソッドが変更されないようにする必要がある場合があります。final キーワードはコードを固定し、特定の実装が子クラスによって変更されないことを保証します。
メソッドをfinalとしてマークすると、子クラスはそれをオーバーライドできなくなります:
<?php
class BankAccount {
protected $balance;
public function __construct($balance) {
$this->balance = $balance;
}
final public function getAccountId() {
return "ACC-" . spl_object_id($this);
}
}
class SavingsAccount extends BankAccount {
public function addInterest() {
return $this->balance * 0.05;
}
}
$account = new SavingsAccount(1000);
echo $account->getAccountId();
出力:
ACC-1getAccountId() メソッドは、SavingsAccount やその他の子クラスでオーバーライドすることはできません。これにより、すべてのアカウントタイプで一貫性を保つ必要がある重要な機能が保護されます。
クラス全体を final としてマークして、継承を一切禁止することもできます:
<?php
final class SecurityManager {
public function authenticate($user) {
return "Authenticating $user";
}
}
$security = new SecurityManager();
echo $security->authenticate("admin");
出力:
Authenticating adminいかなるクラスも SecurityManager を継承することはできません。これは、セキュリティに敏感なクラスや、クラスの設計が完了しており、変更すべきではない場合に役立ちます。
重要なポイント: 継承を許可しつつオーバーライドを防止するためにメソッドに final を使用するか、あるいは継承を完全に防止するためにクラスに使用します。これは、設計通りに正確に動作する必要がある重要なコードを保護するためのツールです。
チャレンジ
簡単final キーワードを使用して、子クラスによって重要な機能が変更されないように保護する方法を示すドキュメント管理システムを構築しましょう。
特定のセキュリティ機能がロックされた状態を維持しながら、さまざまなドキュメントタイプを管理するために連携する3つのファイルを作成します。
Document.php— すべてのドキュメントのベースとなるDocumentクラスを定義します。このクラスには、public な$titleプロパティと protected な$authorプロパティが必要です。コンストラクタは title と author の両方を受け取ります。"Signed by: " . $this->authorを返すfinalメソッドgetSignature()を含めてください。このセキュリティ機能は、子クラスによって変更されてはなりません。また、"Document: " . $this->titleを返す通常のgetContent()メソッドも追加してください。Report.php—Documentを継承するReportクラスを定義します。ファイルの先頭で Document ファイルを読み込んでください。このクラスには$departmentプロパティを追加します。コンストラクタは title、author、department を受け取る必要があります。最初の2つにはparent::__construct()を使用し、その後に department を設定します。getContent()メソッドをオーバーライドして、"Report: " . $this->title . " (" . $this->department . ")"を返すようにします。Reportクラスは、finalなgetSignature()メソッドを、変更することなく継承します。main.php— Report ファイルを読み込みます。ドキュメントのタイトル、著者名、部署の3つの入力を受け取ります。これらの値を使用してReportオブジェクトを作成します。1行目にgetContent()の結果を出力し、2行目にgetSignature()の結果を出力してください。
このチャレンジは、final メソッドがいかに重要な機能を保護するかを示しています。署名の生成はすべてのドキュメントタイプで一貫性を保ちますが、getContent() のような他のメソッドは各子クラスでカスタマイズできます。Report はコンテンツの表示方法を変更できますが、署名の形式は親クラスでロックされています。
チートシート
final キーワードは、クラスやメソッドが子クラスによって変更されるのを防ぎます。
Final メソッド: メソッドを final としてマークすることで、子クラスがそのメソッドをオーバーライドするのを防ぎます:
<?php
class BankAccount {
protected $balance;
final public function getAccountId() {
return "ACC-" . spl_object_id($this);
}
}
class SavingsAccount extends BankAccount {
// getAccountId() をオーバーライドすることはできません
}
Final クラス: クラス全体を final としてマークすることで、いかなる継承も防ぎます:
<?php
final class SecurityManager {
public function authenticate($user) {
return "Authenticating $user";
}
}
// SecurityManager を継承できるクラスはありません
継承を許可しつつ特定の機能を保護するにはメソッドに final を使用し、継承を完全に防ぐにはクラスに final を使用します。これは、セキュリティに敏感なコードや、実装の一貫性を保つ必要がある場合に役立ちます。
自分で試してみよう
<?php
require_once 'Report.php';
// 入力を読み込む
$title = trim(fgets(STDIN));
$author = trim(fgets(STDIN));
$department = trim(fgets(STDIN));
// TODO: 入力値を使用してReportオブジェクトを作成する
// TODO: getContent()の結果を出力する
// TODO: getSignature()の結果を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。