はじめに
メタップス(Metaps)アドベントカレンダー第9日目の記事です。
学生時代、カプセル化を学びました。インターフェースはOOPの設計のためにオブジェクトとオブジェクト間の規約を定義するためのものであり、情報隠蔽はオブジェクト内部のアクセスを防ぐために使用すると理解はしていましたが何か雲をつかむような言に感じられました。
実務でOOPの経験が積み重なってドメイン駆動設計を少し勉強してからカプセル化という概念について理解が少し増えました。
私が理解している内容が珍しいと思ったので投稿します。
オブジェクトとは?
- 飼い主と犬の関係を思い出してみましょう。
- この関係で「飼い主」というオブジェクトがあり、「犬」というオブジェクトがあります。
- オブジェクト間では相互作用があります。
相互作用
- 飼い主が「座れ」と言うと犬は座ります。
- 飼い主が人形を投げると、犬は人形を咥えてきます。
- 飼い主が犬を連れて散歩に出ると、犬は自分で歩きます。
飼い主が犬について知る必要のないもの
- 座れと言われ、脳がこの命令を神経で処理し、脳の判断を通じて筋肉の動きを調節すること。
- 飼い主が人形を投げるとき、犬が走り出し、犬が噛んでくる過程。
- 犬を連れて散歩に出る時、犬がどのように4足歩行をするのか歩く時にバランスを取るのか。
外部からオブジェクトを見る時
- 飼い主と犬の相互作用で飼い主は犬に命令を下すだけで、犬の体で起きる多くの動きがどのように動作するのか飼い主は知る必要がありません。
- 犬と飼い主の間の相互作用で飼い主は犬に筋肉の動き、脳の判断、神経物質の伝達を細かく定義する必要がなく、犬に命令だけ下せば良いです。
- 外部からオブジェクトを見るとき、オブジェクトに命じるコマンドさえ分かれば、オブジェクトの相互作用を構成することができます。オブジェクト内部で起こる動作は知らなくてもオブジェクトに命令するだけでオブジェクトから欲しいものを得ることができます。
- プログラミング言語でのオブジェクトもオブジェクトを操作できるようにパブリック メソッドを提供します。パブリック メソッドで命令だけ下せばいいし、命令を受けてオブジェクト内部で行われるコードについては具体的にどのように動くのかは分からなくてもいいです。
- これにより、オブジェクトの重要な特徴を解ります。 オブジェクトは、「オブジェクトを使用できるコマンドと、そのコマンドを実行するために外部に公開する必要のない内部動作で構成されている」ということです。
カプセル化の二つの要素
- インターフェース
- 情報隠蔽
インターフェースとは?
- ここでいうインターフェースはOOP言語の文法インターフェースを意味するものではないです。
- インターフェースとは、オブジェクトとオブジェクトの間の相互作用を定義する規約です。
- インターフェイスの構成は通常、「指示」と「結果」で構成されます。
インターフェースの例
- 飼い主が「座れ」と言った時(命令)、犬が座るの(結果)がインターフェースです。
- 飼い主が人形を投げた時(命令)、犬が人形をくわえてくるの(結果)がインターフェースです。
- 飼い主が犬を連れて散歩をする時(命令)、犬自らで歩いて飼い主についていくの(結果)がインターフェースです。
インターフェイスはオブジェクトに対するコマンドです。
- 犬というオブジェクトは「座れ」という命令を実行して座る動作をします。
- 犬というオブジェクトは「飼い主の半径Xメートル内の任意の地点に人形を投げる」という命令を行い、人形が落ちた地点に駆けつけ、人形を噛んで飼い主にくわえてきます。
- 犬というオブジェクトは「Nメートルの首輪をして散歩する」という命令を実行し、飼い主が移動しても飼い主の周辺半径Nメートル内にあります。
情報隠蔽とは?
- オブジェクト内部で起こる動作で、オブジェクトの外部では知る必要はないことです。
- 飼い主は犬の内部で起こっていることを知る必要はなく、犬は飼い主の命令に従って内部に処理する動きが情報隠蔽です。
情報隠蔽の例
- 飼い主の「座れ」という言葉を耳で聞いて脳が認知して脳が筋肉に信号を伝達して座る行動をさせる犬の内部の過程です。
- 飼い主が人形を投げる時、犬が人形を投げた位置を目で見て脳が位置を測り、筋肉を動かして走って行って人形を噛んで飼い主の位置に来る、犬が判断するすべての犬内部の処理です。
- 飼い主が犬を連れて散歩に出る時、4足歩行のバランスを合わせて移動する過程で、飼い主が率いる位置通りに歩くために犬の小脳がバランスを維持するために処理する過程、各筋肉を調節する過程です。
OOP言語でカプセル化を実現してみよう
- 上記の説明とは少し違います。説明のために作成したコードです。ご理解ほどよろしくお願いします。
- protectedとprivateメソッドは省略しました。
class Dog
{
private bool $sitStatus = false;
private ?int $positionX = null;
private ?int $positionY = null;
private ?Target $target = null;
private bool $biteingTargetFlag = false;
// constructメソッド(省略)
public function sit(): void
{
$this->sitStatus = true;
}
public function isSit(): bool
{
return $this->sitStatus;
}
public function standUp(): void
{
$this->sitStatus = false;
}
public function setTarget(Target $target): self
{
$this->target = $target;
return $this;
}
public function bringTo(Owner $owner): Target
{
$this->moveTo($this->target->positoinX, $this->target->positoinY);
$this->biteingTargetFlag = true;
$this->moveTo($owner->positoinX, $owner->positionY);
return $target;
}
public function getStatuses(): array
{
return [
'sit_flag' => $this->sitStatus,
'x_position' => $this->positionX,
'y_position' => $this->positionY,
'target' => $this->target
];
}
// privateやprotectedメソッドコード(省略)
}
「座れ」命令の例
$this->dog->sit();
-
$dog
オブジェクトに座るように指示します。 -
$dog
オブジェクトを利用する人は、$sitStatus
の変数がどのように変わるのかを知る必要はありません。 -
isSit
メソッドまたはgetStatuses
メソッドを通じて犬が座っているかどうかを確認することができます。
ボールを投げる命令の例
public function throwItem(Target $ballObj)
{
$ballObj->setRandomDirection()->setDistance(50)->move();
$this->dog->setTarget($ballObj)->bringTo($this);
}
- 飼い主が投げたボールを犬が咥えてくる命令を下しましょう。 飼い主オブジェクトは
throwItem
メソッドを通じてボールを投げる命令を下します。 - これは犬に投げたボールを飼い主の位置にくわえて来いという指示でもあります。 したがって、
throwItem
メソッドは犬がボールを咥えるものにしてsetTarget($ballObj)
オーナーの位置に移動bringTo($this)
させる動作の意味です。 - この時、
$dog
オブジェクトが咥えてくるターゲットは$ballObj
になり、ボールを保有してから飼い主に来ることになります。この動作によって$dog
オブジェクトの位置情報が飼い主の位置に変更されます。 - 飼い主が犬に命じる時、飼い主オブジェクトの
throwItem
メソッド内部の動作と手順を理解する必要はありません。$ownerObj->throwItem($ballObj);
で、飼い主オブジェクトはボールを投げるよう命令だけを下せばいいです。 - また、犬の動作を指示するときも、「ターゲット」と「飼い主のオブジェクト」だけを指定します。 飼い主は犬に咥えてくるよう命令するだけで、犬の位置がどう変わるのか気にしなくてもいいです。
OOP言語の文法特徴
インターフェース
- インターフェイスは、オブジェクトを使用する取扱説明書です。
- オブジェクトは、オブジェクト内部のパブリック メソッドの組み合わせとして使用できる定義される必要があります。 オブジェクトのパブリック メソッドに適切なインプットベリューを入れると、「インプットベリューに対する結果ベリューがどんなものか分かる」というのがインターフェースの本質です。
- オブジェクトを使用することがインターフェイスの本質であるため、文法的なインターフェイスはパブリックメソッドのみで構成されます。パブリック メンバー変数の場合、オブジェクトを使用する方法で提供されるものではないので、オブジェクトの取扱説明書となるべきインターフェースの要素に適していない。
- 文法的なインターフェースは、命令と命令を指示するための因子のタイプと命令の結果が示すバリューのタイプを示します。 このためにOOP言語でインターフェースはパブリックメソッドと因子のタイプとリターンタイプを使用できるように作られました。
- 文法的なインターフェースは、パブリック メソッドの形を文法的に制約する役割もあります。 文法的なインターフェースは、オブジェクトを作成する際に必ずしも必要なものではありません。
情報隠蔽
- オブジェクトの外部からオブジェクトを使用するために定義されているメソッドと内部で処理するために使うメソッドを
public
とprivate
/protected
メソッドに区分します。 -
private
またはprotected
メソッドは内部処理のために使用され、public
メソッドはオブジェクトの外部でオブジェクトを扱うために提供される命令語です。
意味論的プログラミング
単純性
- インターフェイスはオブジェクトの取扱説明書ですので、インターフェイスを定義する際にはオブジェクトの使い方をわかるように意味を示すメソッド名を付けることが重要です。
- オブジェクトを使用する側では、犬にターゲットを指定して咥えてくるよう命令することだけで十分です。 犬の動きの詳細をすべて指定する必要はありません。 また、インターフェースは取扱説明書なので使い方は単純なほうがいいです。 犬がボールを咥えてくるまでの過程をいちいち記述すると、オブジェクトの使い方が複雑になります。単純な命令で表すのが良いので犬がボールを咥えてくるまでの詳細な手順は、情報隠蔽を通じてオブジェクト内部に隠すようにします。
- 人間の言語は一つの現象を様々な表現で表すことができます。 人形を見て、人形に向かって走り、人形をくわえて、再び飼い主に走ってくる犬の行動は「人形を咥えてくれ」という一言で表現できます。 様々な具体的過程の記述しなく、単純な表現だけで過程を説明するのが意味論の力です。 意味論的な単純化は、オブジェクトをより単純に使用する方法です。
- 「オブジェクトを作成する」というのは、「オブジェクトの取扱説明書をパブリック メソッドで構成する」という言と同じ意味します。オブジェクトを扱う時、直感的にどのようなメソッドを使用すれば、希望する動作が行われるかを分かるようにメソッドを定義するのがy重要です。
組合性
- メソッドに複数の因子をあったら、メソッドの役割を直観的に理解しにくくなり、因子の個数と順番も把握しにくくなります。 したがって、複数の因子を受けなければならない場合、複数のメソッドに分離して因子を受ける方がいいです。
- メソッド名に応じる機能は1対1対応となるように定義するのがいいです。 メソッドを実行した時、メソッド名が表す意味よりも多くの影響があれば、メソッドがオブジェクトにどのぐらいの影響を与えるのかわかりにくいです。 また、意味的にオブジェクトを使いにくくなるので、メソッドの名前よりも多くの影響を与えると、複数のメソッドに分けて、可能な限りメソッドの機能とメソッド名が1対1で対応されるように名前を付けることをお勧めします。
- 飼い主がボールを投げる行動をする時、犬にターゲットを指定して咥えて来いという動作をさせる
$this->dog->setTarget($ballObj)->bringTo($this);
コードは複数のメソッドが集まって「投げる」という意味を形成しています。 - 単語と単語を組み合わせて文章を作り出すように、メソッドとメソッドを組み合わせて結果を作り出すのがプログラミングです。 メソッドの組み合わせで一つの意味を形成できるようにメソッドを定義します。
プログラミングは作文である。
- プログラミングはライティングだと思います。 オブジェクト指向は、文章を書くための単語を定義し、文章を定義するための優れるツールを提供します。
- まるで文章を読むように読み上げられるプログラミングこそ可読性が良く理解しやすく管理しやすいコードを作り出します。
- オブジェクトとメソッドの組み合わせでコードを作り、まるで素敵な文章を書くかのようにコードを作ってみましょう。
ドメイン駆動設計
- ドメイン駆動設計の鍵は、ユビキタス言語を作成することです。
- ユビキタス言語とは、コンピュータシステムを設計するためにシステムを作る企画者、運営者、デザイナ、開発者などシステムを使用する様々な人がお互いに共有できる用語でビジネス領域のロジックを論理的な関係が合致するよう具体的に概念を分けていくことです。
- オブジェクト指向を通じてユビキタス言語に対応するオブジェクトとメソッドを作り、これを組み合わせてプログラムを作るのがドメイン駆動設計だと思います。
- オブジェクト指向のカプセル化と意味論的プログラミングに対する理解は、ドメイン駆動設計を作っていく基礎概念になります。