結局、クラス・インスタンスって何かわかる?私はわからない。

..

面接の技術質問でもありがちな「クラスとインスタンスについての説明」を試みます。

説明的な文体を取りますが、教えるつもりなどではなく、あくまで己の説明能力を試したいという次第でございます。

クラスって何

クラスを使ってプログラミングするのがオブジェクト指向プログラミングです。

じゃあクラスは何かといえば、簡単にいえばデータにまつわるメソッドや情報をひとまとめにしたものです。

Progateで勉強したとき出てきましたね。値を取り出す「ゲッター」とセットする「セッター」みたいなやつ。これらは単なるデータの出し入れですが、Webアプリケーションにおいてはもっといろいろやることがあります。いろいろメソッドを書きます。そして情報ごとに必要な処理は違いますから、UserPostといった種類があるごとにクラスを作るわけです。

その中で、「どういった情報が渡ってくるのか」「どの情報をどの程度オープンにするのか」「情報をどう加工するのか」といったことをクラス内に書きます。クラス変数、インスタンス変数、メンバメソッド等々のもろもろですね。

これらはいうならば「情報についての情報」です。

class UserService
{
  public function createUser(array $request) // 配列をもとに生成されるよ、という情報
  {
    $user = new User();
    $user->name = $request['name'];
    $user->roll = $request['name'];

    $user->save();
  }

  public function updateUserRoll(User $user, int $roll) // roll という値が更新されることがあるよ、という情報
  {
    $user->update([
      'roll' => $roll
    ]);
  }
}

なんでクラス化するのか

なぜ、ひとつのファイルにメソッドを詰め込むだけではなく「クラス」という仕組みが必要なのか。これはデータの安全性を高めるためですね。

クラス内のメソッドや変数には、「外部からは参照できない」「外部から参照はできるが、更新はできない」などの様々な制約を課すことができます。これがやりたいんです。

扱っているのは何らかのデータですから、意図せず値が書き換わったり、消えてしまったりなどしては困ります。
そういった心配をなくすために、言語開発者の皆様は修飾子(public、private、const等々)によるアクセス制限や、元々はなかったプロパティを処理中に生やすのを禁止する、といった仕組みを実装してくださっているんですね。

なお現状のPHPでは、タイポ等で未定義のプロパティを取ろうとすると、その瞬間に定義されてnullが言い渡されます。プラグイン等でタイポ対策すればある程度防げるが完璧ではない(過去形と現在形の区別など)

dd($user->neme); // null

(エラーになりなさいってお母さんいつも言ってるよ)

インスタンスって何

じゃあインスタンスとは。インスタンス化する目的は。

これを説明するためには、変数というものの実態をさわり程度に理解するといいのかもしれません。
変数というのはとどのつまり、コンピュータのメモリ上に配置されたデータのアドレスです。

メモリというパーツについては、よく作業机と例えられます。作業机が大きければ大きいほど、置けるものや、同時に進められる作業の数が増えますね。メモリ容量が大きければ大きいほどパソコンは使いやすくなります。

この例えでいうなら、変数化というのは「作業机にものを置くこと」なんですね。

$number = 2;

これはつまり、integer型の値「2」をメモリ上に配置する、という意味です。$numberという名前は、実際には「2」が置かれているメモリ上の場所(アドレス)を示しているわけですね。少なくともコンピュータはそう認識してます。

人間からすれば、例えば「ページ番号($page_number)」とかそういう名前を付けて、その数字が意味するものを表すことがあるわけですが、コンピュータにしてみれば単に「場所(メモリ領域)」と「中身(値)」があるだけで、その値が何を意味しているか等はどうでもいいのです。

閑話休題。

クラスをインスタンス化するときは、変数でインスタンスを受けますね。
そして変数とは、データがメモリ上に配置されること。

ということは「インスタンス化」は「クラスに意味づけられたデータがメモリ上に展開されること」ということになります。
これからクラスのメソッドに値を渡してあーだこーだ作業するわけですから、作業机の上に乗っけるのは当然ですね。「クラスはnewして初めて使えるようになる」といった説明も目にしますが、つまりそういうことです。

クラスからインスタンスが生成されるときは、必ず何か値を受け取ることと思います。
先程クラスの中身を「情報についての情報」と書きましたが、インスタンス生成時に渡されるのは情報そのものです。
20, さしづめ太郎, 本を読むこと」みたいな具体的な情報。
これが「age, name, hobby」といったプロパティ(「情報の意味」という情報)とガッチャンコされることで、意味と情報が結び付けられます。

// ただの値だったものに
$request = [
  'age' => 20,
  'name' => `さしづめ太郎`,
  'hobby' => `本を読むこと`
];

// (アプリケーション上における)意味が与えられる
$user = new User;
$user->age = $request['age'];
$user->name = $request['name'];
$user->hobby = $request['hobby'];

// 💻 < Userの情報なんですね!理解

そして変数$thisにインスタンス自身が格納され、インスタンスはメンバメソッドや各種クラス変数へのアクセスが可能になります。

フレームワークの中身もおんなじ

教材にありがちなAnimalからDogbowbowとかやってても、実際にフレームワークに手を出した時に「コントローラー」や「モデル」というクラスの役割について理解しやすくなるか、といえばそうでもありませんでした。実際に開発で使用したり作ったりするクラスというのは、別に「Animal」や「User」のようなわかりやすい「もの」ではありません。もうちょっと抽象的なものです。

たとえば筆者の場合「サービスクラス」というものを最初に取り扱った際なども、よくわかりませんでした。
プロパティもなにもなく、処理をぶち込んだものでしたからね。とてもAnimalからDogbowbowの延長線上にあるものという気がしない。

でも同じなんです。サービスクラスなど、メソッドがあるばかりで何も情報を渡さないままnewするクラスに関しては特にイメージが付きづらいのですが。要するに$thisという変数にインスタンスが格納されて、彼だけがクラスのprivateメソッドやprivate変数等に対するアクセス権を持っているというだけの話なのです。

use App\Services\UserService;

class UserController 
{
  private UserService $service;

  public __construct()
  {
      // 💻 < $this は UserControllerのインスタンスなので、private変数にもアクセスしてヨシ!
      $this->service = new UserService;
  }

  public function create(Request $request)
  {
      // 💻 < $this->service は UserServiceのインスタンスなので、publicな変数やメソッドにだけアクセスしてヨシ!
      return $this->service->createUser($request);
  }

このコンストラクタまで実行されたら、コントローラークラスにとっての$this->serviceとサービスクラスにとっての$thisは同じ。
しかしコントローラークラスで$this->service->XXXXXXとアクセスできるのは、サービスクラスがpublicにしてくれているものだけ。サービスクラスの全てにアクセスできるのは、サービスクラスのインスタンスが入っている$thisのみ。

そういうことなんですね。


私が話せるのはここまでだ。
まだ全然なんもわかっちゃいないのがおわかりいただけたかと思います。

newした際の挙動やメモリの使い方などは言語ごとに違うようですし、正直そんなところまで一度に勉強できる気はしません。それ以前の問題が山積みですし(ポートフォリオが完成してないとか、DockerとAWSの本読まなきゃ、とか)、正直あとからもう少し理解を深めて編集しようにもますます深みにハマりそうで、きりがありません。いま完璧にわかろうとしてはいけない。

とはいえ低レイヤーよりの知識に対して興味は増すばかりです。Goとかやりたいですしね。
がんばっていきたいですね。