diff --git a/src/pages/tips/object-oriented.mdx b/src/pages/tips/object-oriented.mdx index 81b4071..55b13f7 100644 --- a/src/pages/tips/object-oriented.mdx +++ b/src/pages/tips/object-oriented.mdx @@ -13,6 +13,13 @@ # {title} +スマホのボリュームの上げ下げをするために、わざわざ蓋を明けてこの端子を触ってね・・・ということはないですよね。 +大抵は音量を操作するためのボタンが用意されて、そこを操作してもらううことになると思います。 + +今のは極端な例ではありましたが、プログラミングの世界でも同様のことが起こっています。 + +オブジェクト指向とはこのような危険な操作を隠して、ユーザーが安全に操作できるようにするための設計思想です。 + ## TOC ## 大事な用語 @@ -813,6 +820,300 @@ ### staticなメソッド +通常のメソッドは、インスタンスを通して呼び出す必要があります。 +これに対してstaticなメソッドはインスタンスを通さずに、クラス名を使って呼び出すことができます。 + +```php + 私はMyClassです! +``` + +
+ +staticなメソッドは一見通常の関数と同じ用に見えます。 +しかしクラスの中に定義されているため、そのクラスの [アクセス修飾子](#敢えてアクセス制限をかける-アクセス修飾子) を気にせず、自由にアクセスすることができます。 + +```php +data = $data; + + return $instance; + } + + public function display() { + var_dump($this->data); + } +} + +$instance = MyClass::fromData('Hello, World!'); +$instance->display(); +// => string(13) "Hello, World!" + + +function makeMyClass($data) { + $instance = new MyClass(); + + // public なプロパティではないため、外部からアクセスできない + $instance->data = $data; // Uncaught Error: Cannot access protected property MyClass::$data + + return $instance; +} + +$instance2 = makeMyClass('Hello, World!'); // エラー +``` + +
+ +通常のメソッドと同様に、staticなメソッドもオーバーライドすることができます。 + +```php + 私はBaseClassです! + +DerivedClass::introduce(); +// => 私はDerivedClassです! +``` + +### どのメソッドを呼び出すかを指定する (self, static, parent) + +当然ではありますが、staticなメソッドから別のstaticなメソッドを呼び出すこともできます。 + +```php + 私はBaseClassです! +// 私はBaseClassです! +``` + +
+ +今のコードでは、`introduceTwice()` メソッドは、`BaseClass::introduce()` を呼び出しています。 + +ところで、この`BaseClass`が継承されて、`DerivedClass`が作られたとします。 +そのときには自己紹介をする`introduce()` メソッドは、きっとオーバーライドされるでしょう。 + +```php collapse={4-11} add={14-18} add={"意図した動作ではない?":25-30} + 私はBaseClassです! +BaseClass::introduceTwice(); +// => 私はBaseClassです! +// 私はBaseClassです! + +DerivedClass::introduce(); +// => 私はDerivedClassです! +DerivedClass::introduceTwice(); +// => 私はBaseClassです! +// 私はBaseClassです! +``` + +
+ +この状態では、まだ問題が残っています。 + +`introduceTwice()` メソッドは`BaseClass::introduce()` を呼び出しているため、`DerivedClass` から呼び出したときも`BaseClass::introduce()` が呼び出されてしまいます。 + +これを、`DerivedClass::introduce()` を呼び出すようにするにはどうしたらいいでしょうか? +愚直に、`introduceTwice()` メソッドをオーバーライドして、`DerivedClass::introduce()` を呼び出すようにするしかないのでしょうか? + +```php collapse={3-13,25-29} add={19-22} {"これも1つの解決策":30-35} + 私はBaseClassです! +BaseClass::introduceTwice(); +// => 私はBaseClassです! +// 私はBaseClassです! + +DerivedClass::introduce(); +// => 私はDerivedClassです! +DerivedClass::introduceTwice(); +// => 私はDerivedClassです! +// 私はDerivedClassです! +``` + +
+ +今回の問題の根本は、継承されたクラスからメソッドを呼ぶときに、どの時点のメソッドを呼び出すかを指定する手段を知らない状態だったことにあります。 +これを解決するためのキーワードが、`self`、`static`、`parent` です。 + +```php "parent" "self" /static(?=::)/ add={8-12,14-17,30-33} + 私はBaseClassです! +BaseClass::introduceSelf(); +// => 私はBaseClassです! +BaseClass::introduceStatic(); +// => 私はBaseClassです! + +DerivedClass::introduce(); +// => 私はDerivedClassです! +DerivedClass::introduceSelf(); // オーバーライドされていないため、BaseClass::introduce() が呼び出される +// => 私はBaseClassです! +DerivedClass::introduceStatic(); +// => 私はDerivedClassです! + +DerivedClass::introduceParent(); +// => 私はBaseClassです! +``` + +
+ +つまり意図した`introduceTwicw()` メソッドを定義するためには、`self` ではなく `static` を使う必要があったということになります。 + +```php del={9-10} add={11-12} + 私はBaseClassです! +BaseClass::introduceTwice(); +// => 私はBaseClassです! +// 私はBaseClassです! + +DerivedClass::introduce(); +// => 私はDerivedClassです! +DerivedClass::introduceTwice(); +// => 私はDerivedClassです! +// 私はDerivedClassです! +``` + +staticなメソッドから別のstaticなメソッドを呼び出すときには、`self` と `static` のどちらを使うかで、呼び出されるメソッドが変わることがわかります。 + ## よく見るコード例 ### 初期化のための静的メソッド