Chapter 06

第六章 class とかいうやつ

kuramapommel
kuramapommel
2021.04.24に更新

振る舞いをまとめるために class を使う

オブジェクト指向プログラミング を学習していくと class とかいうやつに出会うかと思います

class とはなにか」という話はそう簡単に説明できるものではないので、ここではひとまず 「とある責務を果たすためにデータや振る舞いを凝集させたモノの設計図」 とでも表現しておきましょう

いきなり難しい話になったように感じるかもしれませんが安心してください、ゆっくり解説していきます

まず、 どうしていきなり class の話をし始めたのか ですが、それは class が前の章でいう 別のアプローチ の回答になるからです

試しに WEAPON_TYPE.BOW の分岐による振る舞いをもとに、 Bow class を書いてみます

class Bow {
  // 武器種名のプロパティ
  public readonly name: string = "弓"
  
  // 攻撃を行うメソッド
  public attack(): void {
    console.log("弓で攻撃")
  }
}

前の章で話していた 振る舞いを何かでまとめる ことが class を使うことで表現できていることにお気づきでしょうか
class はこのように 構造体 としての役割も持つため、データ(ここで言う name )や振る舞い(ここで言う attack )を構造体としてまとめることができます

本線の話からはちょっと逸れますが、次の章からの解説のために class について知っておく必要があるので、本章後半では class についてもうちょっと詳しく解説していきます

なお class はオブジェクト指向プログラミングにおいて非常に重要な役割を担っていますが、その概念を理解するのはとても難しく、それらを解説するだけでも何ページも必要になってしまうため、今回は飽くまで 「場合」を表現するポリモーフィズムを解説するのに必要な部分だけ の紹介とさせていただきます

class とは

先述の通り、 class とは 「とある責務を果たすためにデータや振る舞いを凝集させたモノの設計図」 です

どういうことでしょう
噛み砕いて解説しますね

データや振る舞いを凝集させたモノ

まずは データや振る舞いを凝集させたモノ とはどういうことかですが、
現実世界のものに例えるとイメージしやすいと思います

「やかん」を例にしてみましょう
「やかん」です、水を入れて沸騰させるとキュ〜〜〜と音が鳴るあの「やかん」です

「やかん」は入れられる水の量に限度がありますね
入れ過ぎたら溢れてしまいます

つまり「やかん」は 「容量」というデータや「沸騰したら音を鳴らす」という振る舞いを凝集させたモノ であると言えます

この例は別に「やかん」じゃなくても成り立つでしょう
先のコードに載せた Bow も「名前」というデータや「攻撃する」という振る舞いを持つモノですよね

データや振る舞いを凝集させたモノ とはつまり、 あらゆるモノのこと を指しています
そしてプログラミングの世界では、このモノのことを オブジェクト と呼んだりします

あ、 凝集 については後ほど解説しますね

とある責務

次は とある責務 についてですが、まずは 責務 という言葉を weblio 先生 に教えていただきましょう

せき‐む【責務】
責任と義務。また、果たさなければならない務め。「責務を負う」

そのとおりです

「やかん」の責務はなんでしょう
「やかん」の責務は 中の水が沸騰したら、音を鳴らしてそのことを知らせること です

水を温めるのは「 IH ヒーター」や「コンロ」の責務でしょうか
正しく表現するなら、彼らは「上に置かれたものを温めること」が責務でしょう

このようにモノにはそれぞれ責務があります
それが とある責務 です

凝集

あまり聞き馴染みのない言葉かと思います
ぼくもソフトウェアエンジニアになるまでは一度も耳にしたことのない言葉でした

ということでこちらも weblio 先生 に教わりましょう

ぎょう‐しゅう〔‐シフ|‐シユウ〕【凝集/凝×聚】
[名](スル)
1 散らばったりしていたものが、一つに集まり固まること。「勢力を―させる」

ということです

今回の文脈としては データや振る舞いを凝集させた なので、「データや振る舞いをひとまとまりにした」と捉えられるでしょう
また、このまとまりの強さを表す指標を 凝集度 と呼び、 凝集度は高いほうが良い ものとされています
至るところに部品が散らかっているよりもまとまっている方が使いやすいですからね

ここまでで とある責務を果たすためにデータや振る舞いを凝集させたモノ というのが何かまでは理解できたかと思います
以降はこれを オブジェクト と呼びます

設計図

そしてその オブジェクト の構造を記した設計図が class です
つまり class には、 そのオブジェクトが責務を果たすために必要なデータや振る舞いがまとめられている ことになります

これが class なんですけど、すごくざっくりとした解説なので、より深く知識を埋めたい場合は この辺の本 とか絵が一杯で読みやすくておすすめです(いつの間に第二版出てたんだ、、、知らなかった、、、)

class からインスタンスを生成して使う

突然知識的なお話が多くなってしまったのですが、ここからはちょっと実践的なお話です

まず class は飽くまで設計図でしかないので、モノとして使える状態にするには 設計図をもとに錬成を行い具現化してあげる 必要があります
この錬成を行い具現化する処理を インスタンス化 と呼び、錬成されるモノを インスタンス と呼びます

ほとんどの言語においてインスタンス化を行うためには new という術式を使います

実際に Bow class をインスタンス化して使ってみましょう

class Bow {
  public readonly name: string = "弓"
  
  public attack(): void {
    console.log("弓で攻撃")
  }
}

// new 演算子を用いて Bow をインスタンス化
const bow = new Bow()

// "弓で攻撃" とログに出力する
bow.attack()

// "弓" という文字列を取得する
const weaponName = bow.name
console.log(weaponName)

このように一度 Bow をインスタンス化してしまえば、クラスに記述したメソッドやプロパティを呼んであげることで、何度でも振る舞いを起こすことができます

[余談] 神を創り出さないために注意すること

神クラス という言葉を聞いたことはあるでしょうか

恐れを知らない人間が黒魔術に手を染め、犯した過ちによって創り出されてしまった最恐の class 、それが 神クラス です

神クラスは何でもできます
なぜなら彼は、神なのだから---

っとまあ、冗談はおいておいて、 責務凝集度 を意識することを忘れて開発を進めてしまうとついうっかり神を創り出してしまいます
ついうっかりは我々愚かな人間の数少ない特技ですね

例えば Bow class に下記のような振る舞いを追加したとしましょう

class Bow {
  public readonly name: string = "弓"

  // ハンター の HP を知っている
  private huntersHp: number = 100
  
  public attack(): void {
    console.log("弓で攻撃")
  }

  // ハンターの被ダメージ計算を行っている
  public damaged(power: number): void {
      this.huntersHp = this.huntersHp - power
  }
}

ここで気にしなければならないことは Bow class とは何か」 です
今回で言うのであれば Bow class とは 弓という武器の特性を表したモノ になるので、 ハンターの HP というデータを知っているのは不自然 です

ハンターの HP のデータであれば Hunter class を別途作って、その子が知っている方が妥当でしょう

class Hunter {
  constructor(
    // ハンターのメンバ変数として HP を定義する
    private hp: number
    ) {}
  
  public damaged(power: number): void {
    this.hp = this.hp - power
  }
  
  public getCurrentHp(): number {
    return this.hp
  }
}

何故か新しい class を作ることに億劫になる初学者の方は多く、責務を無視して既存の class に色々と追加しがちですが、神を創り出そうとするその行いはとても愚かな行為です
既存のコードに手を加えるときは、 「その class が持つ唯一の責務を果たすために必要な振る舞いか?」「関係のないところにデータを散りばめようとして凝集度を下げていないか?」 ということを常に意識しましょう

(あれ、、、もしかして、、 Bowattack という振る舞いを知っているのも不自然なんじゃ、、、)