オブジェクト指向でなぜ作るのかを読んで感じたこと
読もうと思ったきっかけ
設計を考える上で理解が必須になってくるオブジェクト指向。
プログラミング手法の種類は3つある。
- 手続き型
- オブジェクト指向
- 関数型
各手法の使い分けがわかっていないので、本書で理解できそうかなと思い読むことにした。
手続き型に関してはもしかしたら書いていないかも。
本書に期待している内容
- オブジェクト指向を使う理由
- オブジェクト指向で開発するために必要な要素が何か(オブジェクト指向の全体像
- デザインパターンの概念(なんとなくわかるけど、人に説明できないから理解できていない
- オブジェクト指向と関数型の使い分け
- UML図の読み書きできるようになりたい
基本的にはネタバレしない程度に、書いてあって「へぇー」となったところを書く
1章 オブジェクト指向はソフトウェア開発を楽にする
オブジェクト指向を提唱した人の名前
オブジェクト指向で書く理由
「オブジェクト指向でなぜソフトウェアを作るのですか?」
誰かにこんな質問をされたなら、筆者はこう答えます
「その理由はソフトウェアを楽に作りたいからです。」と。
なんとなく楽になる気はするけど、具体的には説明できないなー
オブジェクト指向が提唱される以前はどうしていたのか
オブジェクト指向が普及する以前は、対象とするシステム全体の機能をとらえ、それを段階的に詳細化して、より小さな部分に分解していく「機能中心」の開発手法が主流でした。
自分はオブジェクト指向で開発しようと挑戦したが、オブジェクト指向の本質的なやり方を理解出来ていなかったのせいなのか、結局「機能中心」で考えてしまっていたなー
機能中心とモノ中心の開発手法の違い
機能中心
- 仕様変更や機能追加が起きた場合の修正範囲が広範囲になりやすい
- 部品の依存性が高いため、ある改修をした時に関係のないところに影響が出る可能性が高い
- ソフトウェアの再利用が難しい
- 部品の依存性が高いため、影響範囲が広く。ある機能だけ再利用する事が難しい
モノ中心
- ソフトウェアの保守がしやすい
- 部品の独立性を高めているため、影響範囲を把握しやすい
- ソフトウェアの再利用がしやすい
- 部品の独立性を高めているから、影響範囲範囲が小さい。つまり他のシステムでも再利用しやすい
オブジェクト指向の全体像
アジャイル開発手法で全てをひっくるめて実際に使いこなす
OOPを利用して作る
- 再利用部品群(クラスライブラリ・フレームワーク・コンポーネント)
- アイデアを抽出する
- デザインパターン
OOPを図で表現する
- UML(統一モデリング言語)
OOPを上流工程に応用する
- モデリング(業務分析・要件定義・設計)
本にすごくわかりやすい図がある。気になる方は書籍を見てね
このOOPを利用することで、それまで不可能だった大規模なソフトウェアの再利用部品群を作ることが可能になりました。これらはクラスやフレームワークと呼ばれます。
関数型では出来ないのかな。最後に関数型の解説があるので、今は飛ばそう。
またそうした再利用部品群を作る際に利用されたお決まりの設計のアイデアがデザインパターンとして抽出されました
デザインパターンはオブジェクト指向における設計パターン(テンプレート)みたいなイメージか
さらにOOPの仕組みを利用して作るソフトウェア構造を図式表現する方法が考案され、最終的にUML(ユーエムエル:統一モデリング言語)としてまとめられました
UMLを使う事で技術を知らない人でもシステムの全体像を共有できるようになるのかな。
UMLの詳細は後の章で説明があると思うので飛ばす
オブジェクト指向が難しい理由
自分もなんどか挑戦したがオブジェクト指向の学習は難しいと感じた。(自分が勉強の仕方をわかっていなかったのもある)
仕組みがたくさんあって使いこなせていないのがあると思う。
adobe製品のような感じで多機能なのは知っているが、何があるかは知らない状態w
次章以降では細かい仕組みを1つずつ解説が入るとの事なので楽しみ。
オブジェクト指向と現実世界は似て非なるもの
本章で解説しているオブジェクトの仕組み
次の3つはオブジェクト指向の3大要素と言われている。
- カプセル化
- ポリモーフィズム(多態性)
- 継承
本章ではこれらについて解説されている内容になっている。
クラス(カプセル化)
オブジェクト指向の基本的な仕組みはクラスである。
クラス:Class
英単語の意味
分類・種類といった「同種のものの集まり」という意味。
オブジェクト指向プログラミングにおけるクラス(英: class)[1]とは、オブジェクトを生成するための設計図あるいはひな形に相当するものである。抽象データ型の一つ。
クラスから生成したオブジェクトの実体のことをインスタンスという。
この説明からわかるようにクラスとインスタンスは対の関係にあるようだ。
インスタンス(オブジェクト):instance
英単語の意味
instance 実例・実体
instantiation 具体化する
オブジェクト指向言語においては、多くの場合クラスと呼ばれるものを元に作成したオブジェクトの実体を指す。
インスタンスはクラスから生成したオブジェクトの実体の事を指す。
つまりはクラスという設計図が無いとインスタンスは生成できないという事だ。
具体的な例
クラス : インスタンス
プログラム言語 : PHP、JavaScript、C、Go、Flutter、Haskel、Scala・・・
国 : 日本、韓国、中国、アメリカ、イギリス・・・
お酒 : ビール、日本酒、ワイン、ウイスキー・・・
<?php
class Dog
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
/**
* @return string
*/
public function cry(): string
{
return $this->name.'はワンと鳴く';
}
}
//newした事で変数のdogにはインスタンス化されたDogが代入されている
$dog = new Dog('ポチ');
echo $dog->cry();
//出力結果 → ポチはワンと鳴く
p.39
このメソッドを呼び出すことを、相手にメッセージを送って仕事を頼む様子に似ていることからメッセージパッシングと呼びます。
メソッドを呼び出す振る舞いにそういう呼び方があったのか。知らなかった。
ポリモーフィズム(多態性)
英単語の意味
ポリモーフィズム(英: Polymorphism)とは、プログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)についてそれらが複数の型に属することを許すという性質を指す。ポリモルフィズム、多態性、多相性、多様性とも呼ばれる。対義語はモノモーフィズム (Monomorphism)、単態性、単相性で、プログラミング言語の各要素が唯一つの型に属するという性質を指す。
この説明だけではイメージが掴みにくいなぁ。
p.40
この仕組みをひと言で表現すると「類似したクラスに対するメッセージの送り方を共通にする仕組み」と言えます。「相手が具体的にどのクラスのインスタンスであるかを意識せずにメッセージを送れる仕組み」と言ってもよいでしょう
先述のクラスで書いたコードではDogクラスに「鳴く」振る舞いを実装した。
ただ鳴き方というのは動物によって違う。
鳴き声の例
- 赤ちゃん
- オギャー
- 犬
- ワン
- 猫
- ニャー
具体的な例
動物クラス
具体的な動作は定義しない
abstract class Animal
{
// 拡張クラスにこのメソッドの定義を強制する
abstract public function cry(): string;
}
動物クラスから継承して赤ちゃん、犬、猫のクラスを定義する
class Baby extends Animal{
public function cry(): string
{
return 'オギャー';
}
}
class Dog extends Animal{
public function cry(): string
{
return 'ワン';
}
}
class Cat extends Animal{
public function cry(): string
{
return 'ニャー';
}
}
$baby = new Baby();
echo $baby->cry();
//オギャー
$dog = new Dog();
echo $dog->cry();
//ワン
$cat = new Cat();
echo $cat->cry();
//ニャー
これでポリモーフィズムの準備は完了
p.42
ポリモーフィズムはメッセージを送る側が楽をするための仕掛けです。この例でいうと、「cry(泣け/鳴け)」と指示する側は相手が誰でも同じような指示の仕方で済ませることが出来ます。
指示する人の定義して実行してみる
class Trainer
{
public function execute(Animal $animal): void
{
echo $animal->cry();
}
}
$baby = new Baby();
$dog = new Dog();
$cat = new Cat();
$trainer = new Trainer();
$trainer->execute($baby);
$trainer->execute($dog);
$trainer->execute($cat);
//オギャーワンニャー
トレーナはただ指示を出しているだけで、具体的な対象は知りません。
知る必要がないから、ポリモーフィズムはメッセージを送る側が楽をするという目的を達成していますね!
継承
ここは読む前からある程度はイメージが合っていたので、軽く流す
p.43
継承をひと事で表現すると「モノの種類の共通点と相違点を体系的に整理する仕組み」です。
なんとなくわかるが、次の表現の方がよりわかりやすい
p.43
表現を変えると「似たもの動詞のクラスの共通点と相違点を整理する仕組み」とも言えます。
スーパークラス
継承元クラスの事を指す。先程の例だとAnimal。
オブジェクト指向では全体集合をスーパークラスとも呼ぶ。
サブクラス
継承先のクラスの事を指す。先程の例だと、Baby,Dog,Cat。
部分集合をサブクラスとも呼ぶ。
オブジェクト指向と現実世界は似て非なるもの
現実世界の全てをオブジェクト指向に当てはめることは出来ない。
オブジェクト指向の世界と現実世界の違い
オブジェクト指向の世界では、あらかじめてすべての行動をメソッドとして準備しておく必要があります。そしてメッセージパッシングによってメソッドを呼び出すことで、物事が動き始めます。この世界では指示に決して逆らわないのがお約束ごとです。
前提としてオブジェクト指向の世界に存在するものは意志を持っていない。つまり自分で考えて動いてはくれない。指示を出した通りに動く。
現実世界はどうだろうか。
動物には意志がある。感情があるし、体調が悪い時もあり、常に同じような振る舞いが出来ない。結果は状態によって左右されるだろう。
こういった点から現実世界の全てをオブジェクト指向指向に当てはめることは出来ない。
現実世界で起きている仕事を似ているからと言って、ソフトウェアでそれが解決出来るのかはまた別だ。
ソフトウェアで解決できるのは決まりきった仕事だけ。
だからソフトウェア開発をするときは、現実世界の仕事のどこまでをカバーするのかを、明確に決めることが大切だ。
3章はプログラミング言語の歴史
OOPを理解するために、OPP以前のプログラミング技術で何が出来て、どこが限界なのか。を解説されている
読み終えたが、ネタバレというかほぼ本の内容を転載することになるので考慮して書くのが難しいので記事化するのはやめる。
4章 OOPは無駄を省いて整理整頓するプログラミング技術
この章で解説していること
- クラス
- ポリモーフィズム
- 継承
- パッケージ
- 例外
- ガベージコレクション
2章で上記について説明があったが、更に掘り下げて解説されてた
構造化言語では解決できなかったこと
p.77
前章の最後で、構造化言語では解決できない課題が2つあることを書きました、ひとつはグローバル変数で、もう一つは貧弱な再利用です。
OOPの3つの仕組みはまさにこの課題を解決するためのものです
- グローバル変数問題
- 貧弱な再利用
OOPの3つの仕組み
- クラス
- ポリモーフィズム
- 継承
クラス
p.78
クラスは関連性の強いサブルーチン(関数)とグローバル変数を1つにまとめて粒度の大きいソフトウェアを作る仕組みです。従来はバラバラに存在していたサブルーチンとグローバル変数をまとめて整理できます。
自分もOOPを知る以前は、手続き型で書いていたので、毎回処理を探すのが大変だったなぁ。
クラスの何が便利か
- サブルーチンと変数を「まとめる」
- 関連性のあるサブルーチンや変数をクラスにまとめる
- クラスの内部だけで使う変数やサブルーチンを「隠す」
- publicだと外部に公開
- privateだと内部でだけ使える
- 1つのクラスからインスタンスを「たくさん作る」
クラスの効能1 - まとめる
1. 構造化プログラミングによるファイルアクセス処理
厳密には動かないプログラム。あくまでもイメージ
$filiNum = 0;
openFile('hoge');
closeFile();
readFile();
public function openFile(string $pathName){};
public function closeFile(){};
public function readFile(){};
2. クラスを使ってまとめる
final class TextFileReader{
//アクセス中のファイル番号を格納する変数
private int $fileNum;
//ファイルをオープンする
public function open(string $pathName){}
//ファイルをクローズする
public function close(){}
//ファイルから1文字読み込む
public function read(){}
}
2つを見比べて見ましょう。
1の場合は、グローバルに定義しているので変数名やメソッド名に具体的な名前を入れる必要がある。
2の場合は、その中でしか使えないため、名前の重複を気にする必要がない。
クラス名が主語になっているので省略できる。openだけでも意味が伝わる。
まとめると
- メソッドの名前を考えるのが楽になった
- メソッドを探しやすい
- 発見しやすくなるため、結果的に再利用がしやすい
クラスの効能2 - 隠す
クラスの外部から使われていないメソッドはインスタンス変数を隠すことで修正の影響範囲が小さくなる、
内部でしか使われていないことが担保された事で、そこの処理だけ気にすればよくなる。
intelliJを使っているとあまり気にならないけど、昔のエディタとかだとどこで呼ばれているのかが解析されていないと思われる。
そう考えるとこの効果は大きいだろうなぁ。
クラスの効能3 - たくさん作る
p.91
一般的にアプリケーションでは同種の情報を複数同時に扱う場合がよくあるため、この仕組みは非常に強力です。
final class TextFileReader{
//アクセス中のファイル番号を格納する変数
private int $fileNum;
//ファイルをオープンする
public function open(string $pathName){}
//ファイルをクローズする
public function close(){}
//ファイルから1文字読み込む
public function read(){}
}
$textFileReader1 = new TextFileReader();
$textFileReader2 = new TextFileReader();
//これらはお互いに干渉しない
$textFileReader1->open('hoge');
$textFileReader2->open('oop');
変数の種類
変数には3種類ある。
- ローカル変数
- グローバル変数
- インスタンス変数
本に詳細書いてあるけど、記事から拝借。
【Ruby】変数の種類と違い(グローバル変数・ローカル変数・インスタンス変数)
自分の方でも後に記事にしたい。
ポリモーフィズム
p.95
ポリモーフィズムはひと言で表現すると、共通メインルーチンを作るための仕組みとも言えます。
共通サブルーチンは呼び出される側のロジックを1つにまとめますが、ポリモーフィズムは反対に、呼び出す側のロジックを1本化します
共通サブルーチン
呼び出す側が増えても、呼び出される側を修正する必要がない
ポリモーフィズム
呼び出される側が増えても、呼び出す側を修正する必要がない
つまりポリモーフィズムとは「共通メインルーチン」を作る仕組みである。
OOPが登場するまでは共通メインルーチンはなかった。
ポリモーフィズムを利用するためには
呼び出されるメソッドや戻り値の形式を統一する必要がある。
継承やinterfaceを使う。
継承
p.99
継承はひと言で表現すると「クラスの共通部分を別クラスにまとめる仕組み」とも言えます。この仕組みを利用することで、変数とメソッドをまとめた共通クラスを作り、別のクラスからその定義をまるごと拝借することが可能になります
p.102
継承は、クラス定義の共通部分を別クラスにまとめて、コードの重複を排除する仕組みである。
三大要素を活用しよう
- 規則がないと悩んでしまう。悩む時間がもったいない。型にはめて楽をしよう!
- クラスを型として利用する
進化したOOPの仕組み
パッケージ
クラスを更にまとめる「パッケージ」という仕組みがある
ディレクトリを作ってその中にクラスを配置する。ファイルの管理を楽にするための仕組み。
パッケージを切るみたいな話が出たときは、先述のイメージを持てばいい。
例外
p.110
例外をひと言で表現すると「戻り値とは違う形式で、メソッドから特別なエラーを返す仕組み」と言えるでしょう。
特別なエラーの例
- ネットワークの通信障害
- ディスクのアクセス障害
- データベースのデッドロック
障害以外ではファイルの読み込み処理でのEOF検出のように、戻り値として適切な値を返せない場合もあります。
従来のサブルーチンでは
エラーコードを戻り値として使うことで、具体的なエラーの意味を定義づけていた。
これには2つ問題がある。
- エラーコードの判定処理をアプリケーションで確実に行う必要がある
- 判定処理を書き忘れたり、値を間違えると原因の特定が難しくなる
- エラーコードを判定する同じようなロジックがサブルーチンの間で連鎖してしまう
- 呼び出す側のサブルーチンでエラーコードの値を判定するロジックを常に書かないといけない
- 結果的にプログラムのロジックが非常に冗長になる
例外はこれらの課題を解決する仕組み。
例外が解決すること
- 特別なエラーを返すことをメソッドで宣言する
- 例外を宣言しているメソッドを呼び出す時には、例外を処理するロジックを正しく書いていないとプログラムはエラーになる
- 例外の宣言だけ出来る。具体的な例外の処理まで書く必要はない
- 上位のメソッドにエラーを伝えることができる
- エラー処理を書く必要がなくなる
ガベージコレクション
プログラムが削除する処理を書かなくても、必要なくなったインスタンスを削除してくれる仕組み。
インスタンスが大量に生成される = メモリが消費する
詳細は次の章で解説が入るので簡単な内容だけメモ。
ここの仕組みは気になっているので次章が楽しみ。
5章 メモリの仕組みの理解はプログラマのたしなみ
この章で学べること
-
OOPのプログラムが動く仕組み
- 自分の作ったプログラムがどういう仕組で動くか知ろう
-
プログラムの実行方式
- コンパイラ方式
- インタプリタ方式
- 仮想マシン
-
スレッド
-
メモリ管理
-
OOPの仕組みではメモリがどう動いているか
- クラス
- ポリモーフィズム
- 継承
-
ガベージコレクション
結構長い。理解すると面白いし、抑えておく必要のある内容でした。
プログラムの基本的な実行方式
どちらも一長一短で適切に使い分ける
コンパイラ方式
ソースコードを機械語に一括変換してから実行する。
エラーがわかるタイミング
変換するとき。実行する前にわかる。
メリット
- 実行速度が早くなる
- 機械語に変換済のため、解釈する時間がない
デメリット
- 実行するのに手間がかかる
- 実行するにはコンパイル(変換)を行う必要がある。
- エラーが出たら、解消するまで実行ができない。
インタプリタ方式
ソースコードに書かれている逐次解釈しながら実行する。
エラーがわかるタイミング
実行するとき。言い方を変えると実行しないとわからない。
メリット
- 手軽に実行できる
- マルチプラットフォーム(他のマシン・OS)対応しているため、互換性を保てる
デメリット
- 実行速度が遅い
- 逐次解釈をしているから、コンパイラ方式に比べると動作が多い
- 実行しないとエラーがわからない
CPUは複数のスレッドを掛け持ち実行する
p.134
複数のスレッドを同時並行で処理することで、CPUリソースを効率的に利用できる。
【図解】CPUのコアとスレッドとプロセスの違い,コンテキストスイッチ,マルチスレッディングについて | SEの道標
プロセス
コンピュータ上で独立して動いているアプリケーションやそのアプリケーションに対する管理情報をまとめたもの。
プロセスとスレッドとタスクの違いを知ってUnity非同期完全理解に近付く
プロセス間は同じプログラムを使用していない限りはメモリを共有しません。つまりプロセスの数だけメモリは割り当てられていきます。
メモリって共有しているものだと思ってたけど、プロセスごとに決まっていたのね。
確かにintelliJではメモリ割り当ての設定できるし、納得。
スレッド
プロセス内での処理の実行単位。
GithubActionで例えるなら、アクションと同じ意味かな
メモリの話
プログラム実行時には3つのメモリを使い分けている。
個人的にメモリの動きが知れて感動した!
p.134
プログラムのメモリ領域は、静的領域、ヒープ領域、スタック領域の3つに分けて管理する。
メモリ領域
- プログラムの実行時に利用される領域
- 静的領域
- ヒープ領域
- スタック領域
静的領域
使い方
アプリケーション開始時に確保する
格納される情報
- グローバル変数
- 静的変数
次のコードは静的領域が使用される時の一例です。
グローバル変数
/* グローバルスコープの変数 */
$a = 1;
$b = 2;
function Sum()
{
global $a, $b; //globalと宣言する事で、グローバル変数と解釈される
$b = $a + $b;
}
Sum();
echo $b;
//結果は3
静的変数
/* グローバルスコープの変数 */
$a = 1;
$b = 2;
function Sum()
{
static $a, $b; //staticと宣言する事で静的変数と解釈される
$b = $a + $b;
}
Sum();
echo $b;
//結果は2
確保される単位
アプリケーションでまとめて1つ
ヒープ領域
heapの意味は山ほど・たくさん。
アプリケーション(プロセス)に割り当てられたメモリ領域。
intelliJで設定しているのはヒープ領域だったのか。
IDE のメモリヒープを増やす | IntelliJ IDEA
CSV出力のときに、次のエラーをよく見たがこれはヒープ領域が足りていないって事だったのか。
PHP Fatal error: Allowed memory size of xxx bytes exhausted
解決策としてphp.iniのmemory_limitを変更して回避してたなー。
きっとメモリ増やさなくてもプログラムの書き方次第で回避できたんだろうな・・・
使い方
開始時に一定領域を確保し、必要の都度アプリケーションに割り当てる
格納される情報
任意(アプリケーションによる)
確保される単位
システムまたはアプリケーションで1つ
スタック領域
スタック領域はスレッドごとに用意されている。
使い方
LIFO(後入れ先出し方式)
格納される情報
p.137
呼び出したサブルーチンの引数、ローカル変数、戻り先
- ローカル変数
- 関数呼び出しに使う引数
- 関数呼び出し元
確保される単位
スレッドごとに1つ
ここからはOOP固有の話
プログラムが動く基本的の仕組み話はここまで。
ここからはOOPを使って書いたプログラムのメモリの使い方の話になる。
OOPで書いたプログラムはメモリの使い方が従来のとだいぶ異なる。
基本的な仕組みであってもプログラミング言語ごとに使用が少し違うらしい。
本書ではJavaを前提として解説されている。
次からはクラス・ポリモーフィズム・継承の仕組みを使った時のメモリの使われ方が解説されている。
クラス(クラス情報はクラスにつき、1つだけロードする)
p.138
ここでいうクラス情報とは、個々のインスタンスに依存しないクラス固有の情報です。
p.139
クラス情報をロードするメモリ領域は(図5.6)の静的領域に相当する場所です。しかしJavaでは必要なクラス情報を逐次ロードする方式を採用しており、実行時にメモリの配置が変化することから、「静的領域」ではなく「メソッドエリア」と呼びます。
クラス情報はメモリ領域の静的領域(メソッドエリア)に格納されるのか。
インスタンス生成のたびにヒープ領域が使われる
クラス = メソッドエリア(静的領域)
↑
インスタンス = ヒープ領域
p.141
OOPで書いたプログラムは、有限のメモリ領域であるヒープ領域を大量に使って動く
phpでCSV出力を実装したときに次のエラーが出る事が多々あった。
確かLaravelかSymfonyで作られていたはず。
PHP Fatal error: Allowed memory size of xxx bytes exhausted
当時はメモリ増やすか・出力件数を減らしてもらうで解決していた。
今思えば、出力件数毎にインスタンスを生成していた事でメモリが足りなくなっていたのが原因だったのかと理解できた。
変数にはインスタンスの「ポインタ」が格納される
TextFileReaderのインスタンス生成
TextFileReader reader = new TextFileReader();
TextFileReaderクラスのインスタンスはヒープ領域に確保される。
変数renderはどういう情報が格納されるのか
変数の中身によって配置される場所が変わる
- スタックに置かれる
- メソッドの引数
- ローカル変数
- メソッドエリア(静的領域)にあるクラス情報に配置することも可能
こういった理由から変数renderにはインスタンスのそのものを格納しない。
代わりにヒープ領域に作られたインスタンスのポインタが格納される。
ポインタ
ポインタは「メモリ領域の場所を示す情報」です。
本書でも解説があるけど、把握していないので掘り下げて調べた。
ポインタ変数とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
ヒープ領域に確保されたメモリ領域 = 土地
ポインタ = メモリ領域内での住所
変数はインスタンスの住所(ポインタ)が格納されているイメージか。
インスタンスを格納する変数のコピーに注意
変数の中にはインスタンスではなくて、ポインタが入っている事が理解出来ていれば次の挙動も理解できる
変数の中にはポインタが入っているだけで参照しているインスタンスは同じ。
つまりは上書きされてしまう。
final class Person {
/**
* @var string
*/
public string $name;
/**
* @return string
*/
public function name(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
}
$person = new Person();
$bucchi = $person;
$bucchi->setName('ブッチ');
$kitamura = $person;
$kitamura->setName('北村');
echo $bucchi->name().PHP_EOL;
echo $kitamura->name().PHP_EOL;
/*結果
北村
北村
*/
phpで別々のインスタンスとして扱うには次の2つかな?
変数ごとにPersonのインスタンスを生成する
それぞれに対してnewを使う。
$bucchi = new Person();
$bucchi->setName('ブッチ');
$kitamura = new Person();
$kitamura->setName('北村');
echo $bucchi->name().PHP_EOL;
echo $kitamura->name().PHP_EOL;
/*結果
ブッチ
北村
*/
cloneを使う
$person = new Person();
$bucchi = clone $person;
$bucchi->setName('ブッチ');
$kitamura = clone $person;
$kitamura->setName('北村');
echo $bucchi->name().PHP_EOL;
echo $kitamura->name().PHP_EOL;
/*結果
ブッチ
北村
*/
p.148
変数にはインスタンスそのものではなく、インスタンスのポインタ(場所を示す情報)が格納される。
インスタンスを格納する変数を他の変数に代入した場合、ポインタがコピーされるだけで、ヒープ領域にあるインスタンスそのものは変化しない。
完全に理解した!
ポリモーフィズム(ポリモーフィズムは異なるクラスが同じ顔を見せる)
本書の図で見た方がわかりやすいため、詳細は割愛。
また説明の中でもたしなみぐらいに理解していれば言いと記載されている。
継承(継承される情報の種類によってメモリ配置は異なる)
文字だけで表現するにはわかりづらいので、本書に掲載されている詳細は図を見たほうが良い。
p.154
スーパークラスから継承したメソッドとインスタンス変数のメモリ配置は全く異なる。
スーパークラスのインスタンス変数は、ヒープ領域にあるサブクラスのすべてのインスタンスにコピーして保持する。
<?php
class Person
{
/**
* @var string
*/
public string $name;
/**
* @return string
*/
public function name(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
}
class Employee extends Person
{
/**
* @var int
*/
public int $employeeId;
/**
* @return int
*/
public function employeeId(): int
{
return $this->employeeId;
}
/**
* @param int $employeeId
*/
public function setEmployeeId(int $employeeId): void
{
$this->employeeId = $employeeId;
}
}
/*スーパークラスから継承したメソッドとインスタンス変数のメモリ配置は全く異なる*/
/*
* スーパークラスはメソッドエリアに配置されている。
* Personインスタンスはヒープ領域に格納されている。
*/
$person = new Person();
/*
* Personクラスから継承されたインスタンス変数は各インスタンスにコピーされる
* 次のインスタンス変数はヒープ領域にそれぞれ配置されている。
* Employeeのメソッドテーブルはメソッドエリアに配置されている。
* インスタンスとは違い同じメモリ領域を参照している。
*/
$bucchi = new Employee();
$bucchi->setEmployeeId(1);
$kitamura = new Employee();
$kitamura->setEmployeeId(2);
ガベージコレクション(孤立したインスタンスはガベージコレクタが処分する)
ガベージコレクションってなんだ
ヒープ領域に残った不要なインスタンスを自動的に削除する仕組み
複雑な仕組みのため、詳しい仕組みは解説ない。
OOPを使う上でどういう状態のインスタンスがガベージコレクションの対象になるのかを説明してくれている。
誰がガベージコレクションを実行するのか
ガベージコレクタという専用のプログラムが行う。
p.155
このプログラムは適切なタイミングでヒープ領域の状態を調べ、空きメモリ領域が少なくなった事を検知すると実際のガベージコレクション処理を起動します。
なんて素敵な処理なんだ!
ガベージコレクションの対象となる条件
どうやって不要なインスタンスかを判断するかというと「孤立したインスタンスであるか」で判断する。
p.156
根本から辿れないインスタンスがゴミ集めの対象になります
具体的な対象例
- どこからも参照されていないインスタンス
- 循環参照のみでしか使われていないインスタンス
p.158
ガベージコレクタはスタック領域とメソッドエリアから辿れなくなった不要なインスタンスを削除する。
完全に理解した!
まとめ
この章では実際にプログラムが動く流れを丁寧に解説されている。
時間はかかったものの、概念的なものは理解できた。
プログラムとして抑えておく事でパフォーマンスを考慮する事が出来るだろう。
スクラップだと目次がわかりづらいから本にする。