🦁

実装者と利用者の視点を意識してClassを開発する

2021/09/28に公開

クラスのPrivateなフィールド・メソッドはクラスの内部からしかアクセスできず、Publicなフィールド・メソッドはクラスの外部からアクセスできます。クラスの内部はクラスの実装者が意識する部分で、クラスの外部はクラスの利用者が意識する部分となります。

また、クラス(クラスAとする)の内部で他のクラス(クラスBとする)を呼び出している時、クラスAの実装者は同時にクラスBの利用者となります。

このように、クラスは利用者と実装者の視点が入子構造になっています。この実装者と利用者の視点を意識してクラスを設計していくことが重要だという話を説明します。

利用者の視点とは

クラスの利用者の視点とは、文字通りクラスをImprotして利用する人の視点です。

クラスの利用者の視点では、クラスの内部実装を見ることは原則としてありません。普段外部ライブラリーを利用する時はコードの内部実装をみないことがほとんどかと思います。クラスの名称やInterface・ドキュメントからほとんどの挙動を理解します。

また、クラスの利用者は内部の実装を完全に理解していないため誤ったクラスの利用方法をするかもしれません。最悪の場合、クラスの利用者はハッキングによって悪意の挙動を示すかもしれません。

そのため、**「クラスの実装を知らず、間違いを犯すかもしれない人」**を想定してクラスを実装する必要があります。

内部実装のプロパティーをPrivateにする

よくプロパティーを原則Publicにするなということが言われますが、これはクラスの利用者が**「クラスの実装を知らず、間違いを犯すかもしれない人」**を想定していることからきます。

例えば、Rectangle interfaceを継承した Square クラスがあり、高さプロパティーy, 幅プロパティー**x** を持つとしましょう。ここで、Square クラスのx,y プロパティーを直接いじれた場合、x=2, y=3と設定することで正方形ではなくなってしまいます。このように間違った操作を防ぐために、x,yをprivateなフィールドにして、setLengh()などのメソッド経由からx,yといった内部動作にアクセスさせることで、正方形であるということを保証するようにするべきです。

内部のフィールドにアクセスさせるのは、現実の世界で例えると自動販売機の扉を外した状態で設計し、直接手を突っ込ませてジュースを取り出してもらい、正しい金額お金を直接中にいれてもらうように期待するようなものです。

お金を奪っていってしまうかもしれないし、善意の人だとしてもお釣りの計算を間違うかもしれませんし、欲しいジュースをどこから取り出せばいいかわからないかもしれません。

PublicとPrivateを正しく設定するということは、自動販売機の扉の外と、鍵で閉められた扉の内部を区別して設計するようなことです。

隠れた状態変化を起こさない

もう一つ大事なこととして、隠れた状態変化を起こさないことです。これは副作用などとも呼ばれます。

例えば、Translator classに getEnglishAnimalName(animal: Animal)という関数があったとします。この関数は、動物の名前を英語に変化して返してくれそうです。
この関数が以下のような実装であったとしましょう。

class Translator {
   getEnglishAnimalNameToEnglish(animal: Animal) {
      animal.name = tranlatateToEnglish(animal.name)      
      return animal.name
   }
}

この関数は、実際には英語に変化した結果だけでなく、引数として渡したanimal の名前も変えてしまいます。getという接頭辞からは、値を取得することが期待されますが、実際にはset も含まれます。

このようなことは、メソッド名だけからは推測できません。

値を返す以外に状態を変える場合、かつその状態がクラスの利用者に影響のあるものの場合は、必ず関数名からそのことが推測されるようにするか、ドキュメントでそのことを明記しましょう。

Discussion