final キーワード
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 24/91。
継承とメソッドのオーバーライドは柔軟性を提供しますが、特定のクラスやメソッドが変更されないようにする必要がある場合もあります。finalキーワードはコードを固定し、特定の実装を子クラスによって変更できないようにします。
method を final としてマークすると、子 class はそれを override できません。
<?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どの class も SecurityManager を extend できません。これは、セキュリティに関わる class や、設計が完成していて変更すべきでない class に便利です。
重要なポイント: 継承を許可しながらオーバーライドを防ぐにはメソッドにfinalを使用し、継承そのものを防ぐにはクラスに使用します。これは、設計どおりに正確に動作しなければならない重要なコードを保護するためのツールです。
チャレンジ
簡単finalキーワードが、子クラスによる重要な機能の変更をどのように防ぐかを示すドキュメント管理システムを構築しましょう。
異なる種類のドキュメントを管理しながら、特定のsecurity機能を変更できない状態に保つ、連携して動作する3つのファイルを作成します。
Document.php:すべてのドキュメントの基盤となるDocumentclassを定義します。publicな$titlepropertyとprotectedな$authorpropertyを持たせます。constructorはtitleとauthorの両方を受け取ります。"Signed by: [author]"をreturnsする、finalなgetSignature()methodを含めます。このsecurity機能は子クラスによって決して変更されてはなりません。また、"Document: [title]"をreturnsする通常のgetContent()methodも追加します。Report.php:DocumentをextendsするReportclassを定義します。先頭でDocumentファイルをincludeします。このclassは$departmentpropertyを追加します。constructorはtitle、author、departmentを受け取る必要があります。最初の2つにはparent::__construct()を使用し、その後departmentを設定します。getContent()methodをOverrideして、"Report: [title] ([department])"をreturnsするようにします。ReportclassはfinalなgetSignature()methodを継承しますが、変更することはできません。main.php:Reportファイルをincludeします。3つの入力、つまりドキュメントのtitle、author名、departmentを受け取ります。これらの値でReportobjectをCreateします。1行目にgetContent()のresultをPrintし、次に2行目にgetSignature()のresultをPrintします。
このchallengeは、final methodが重要な機能をどのように保護するかを示します。signatureの生成はすべてのドキュメントタイプで一貫したままですが、getContent()のような他のmethodは各子クラスでカスタマイズできます。Reportはcontentの表示方法を変更できますが、signatureの形式はparent classで固定されています。
自分で試してみよう
<?php
require_once 'Report.php';
// 入力を読み取る
$title = trim(fgets(STDIN));
$author = trim(fgets(STDIN));
$department = trim(fgets(STDIN));
// TODO: 入力値でReportオブジェクトを作成する
// TODO: getContent()の結果を出力する
// TODO: getSignature()の結果を出力する
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: PHPオンラインコンパイラ