diff --git a/src/pages/tips/object-oriented.mdx b/src/pages/tips/object-oriented.mdx index 2662312..5c78aa9 100644 --- a/src/pages/tips/object-oriented.mdx +++ b/src/pages/tips/object-oriented.mdx @@ -325,12 +325,12 @@ class User { public $name; - public function __constructor() { + public function __construct() { echo "newされました"; } } -$user = new User(); // __constructor() が呼び出される +$user = new User(); // __construct() が呼び出される // => newされました ``` @@ -343,7 +343,7 @@ class User { public $name; - public function __constructor($name) { + public function __construct($name) { echo "newされました"; $this->name = $name; } @@ -400,7 +400,7 @@ class User { public $name; - public function __constructor($name) { + public function __construct($name) { $this->name = $name; } @@ -458,9 +458,261 @@ ### 敢えてアクセス制限をかける (アクセス修飾子) -ここまで読み進めてきた人に取っては、ソースコードに含まれる `public` というキーワードが気になっているかもしれません。 +ここまで読み進めてきた人に取っては、ソースコードに含まれる `public` というキーワードが気になっているかもしれません。 +これは、どこからでもアクセスすることを許可するという意味の **アクセス修飾子 (access modifier)** と呼ばれるものです。 -### データに依存しない振る舞いを定義する (static) +アクセス修飾子には、いくつかの種類がありますが、主に以下の3つが使われます。 + +| 種類 | 説明 | +| :-- | :-- | +| `public` | どこからでもアクセスできる | +| `protected` | クラスの中と、継承したクラスの中からアクセスできる | +| `private` | クラスの中からのみアクセスできる | + +具体的に見てみましょう。 + +あるユーザーに対応するクラスがあったとして、そのメソッドに成人かどうかを判定する `isAdult()` というメソッドがあったとします。 +また、`birthday()` を呼び出すことが、誕生日を迎えることを表すとします。 + +```php +name = $values["name"]; + $this->age = $values["age"]; + } + + public function birthday() { + $this->age += 1; + } + + public function isAdult() { + if ($this->age >= 20) { + echo "{$this->age}歳なので、成人です"; + } else { + echo "{$this->age}歳なので、未成年です"; + } + } +} + +$user = new User([ "name" => "Alice", "age" => 18 ]); + +$user->isAdult(); +// => 18歳なので、未成年です + +// 2回誕生日を迎える +$user->birthday(); +$user->birthday(); + +$user->isAdult(); +// => 20歳なので、成人です +``` + +
+ +今、すべてのアクセス修飾子が `public` になっています。 +このとき、どのような不都合があるでしょうか? + +```php collapse={6-22} add={"ageはpublicなので、外部から代入できる": 37-38} +name = $values["name"]; + $this->age = $values["age"]; + } + + public function birthday() { + $this->age += 1; + } + + public function isAdult() { + if ($this->age >= 20) { + echo "{$this->age}歳なので、成人です"; + } else { + echo "{$this->age}歳なので、未成年です"; + } + } +} + +$user = new User([ "name" => "Alice", "age" => 18 ]); + +$user->isAdult(); +// => 18歳なので、未成年です + +// 2回誕生日を迎える +$user->birthday(); +$user->birthday(); + +$user->isAdult(); +// => 20歳なので、成人です + + +$user->age = 10; // 年齢詐称 + +$user->isAdult(); +// => 10歳なので、未成年です +``` + +
+ +なんと年齢を詐称することができてしまいました。 +これができるということは、未成年にも関わらずにお酒を飲んだり、タバコを吸ったりすることを許してしまうことになります。 + +これをさせないために、`age` プロパティを `private` にしてみましょう。 + +```php collapse={6-22} "private" {"ageはprivateなので、外部から操作できない": 37-38} +name = $values["name"]; + $this->age = $values["age"]; + } + + public function birthday() { + $this->age += 1; + } + + public function isAdult() { + if ($this->age >= 20) { + echo "{$this->age}歳なので、成人です"; + } else { + echo "{$this->age}歳なので、未成年です"; + } + } +} + +$user = new User([ "name" => "Alice", "age" => 18 ]); + +$user->isAdult(); +// => 18歳なので、未成年です + +// 2回誕生日を迎える +$user->birthday(); +$user->birthday(); + +$user->isAdult(); +// => 20歳なので、成人です + + +$user->age = 10; // Error: Cannot access private property User::$age +``` + +
+ +これで、誕生日を迎える以外の方法で、年齢を変更することができなくなりました。 + +ところで、`User`というクラスから、派生したクラスが作られたとします。 +そこに、`greet()` というメソッドを定義して、ユーザーに挨拶させたいとします。 + +```php add={25-29} "private" +name = $values["name"]; + $this->age = $values["age"]; + } + + public function birthday() { + $this->age += 1; + } + + public function isAdult() { + if ($this->age >= 20) { + echo "{$this->age}歳なので、成人です"; + } else { + echo "{$this->age}歳なので、未成年です"; + } + } +} + +class ExtendedUser extends User { + public function greet() { + echo "こんにちは、{$this->name}さん!"; // Undefined property: User::$name + } +} +``` + +
+ +これはエラーになります。 +なぜなら、基底クラスである `User` クラスの `name` プロパティは、アクセス修飾子が `private` になってなっています。 +そのため、継承先のクラスではこのプロパティにアクセスすることができないからです。 + +これを解決するために、アクセス修飾子を `protected` にしてみましょう。 + +```php "protected" {31-33} {"protectedなので、関係のない場所からは操作できない": 45-46} +name = $values["name"]; + $this->age = $values["age"]; + } + + public function birthday() { + $this->age += 1; + } + + public function isAdult() { + if ($this->age >= 20) { + echo "{$this->age}歳なので、成人です"; + } else { + echo "{$this->age}歳なので、未成年です"; + } + } +} + +class ExtendedUser extends User { + public function greet() { + echo "こんにちは、{$this->name}さん!"; + } +} + +$user = new ExtendedUser([ "name" => "Alice", "age" => 18 ]); +$uset->greet(); +// => こんにちは、Aliceさん! + +$user->isAdult(); +// => 18歳なので、未成年です + +// 2回誕生日を迎える +$user->birthday(); +$user->birthday(); + +$user->isAdult(); +// => 20歳なので、成人です + + +$user->age = 10; // Error: Cannot access protected property User::$age +``` + +
+ +これで、継承先のクラスからは `name` プロパティにアクセスすることができるようになりました。 +`age`も同様に `protected` にしているので、継承先のクラスからはアクセスできますが、関係のない場所からはアクセスできないようになっています。 + +### データを共有する (static 1) + +### データに依存しない処理を定義する (static 2) ## よく見るコード例 @@ -738,7 +990,7 @@ protected $name; protected $age; - public function __constructor($name) { + public function __construct($name) { echo "newされました"; $this->name = $name; @@ -807,7 +1059,7 @@ protected $user; protected $age; - public function __constructor($name) { + public function __construct($name) { echo "私は {$name} です"; $this->name = $name; } @@ -832,7 +1084,7 @@ class ParttimeUser extends User { - public function __constructor($name) { + public function __construct($name) { echo "吾輩は {$name} です"; $this->name = $name; }