移動: トップ > ラボ > プログラミングのアイディア > 人に仕事を頼む感覚でクラスを作る
クラスを作るタイミング | クラスとは会社の中で働く人
履歴:
統計:
目次:
このページの最終更新日: 2012/05/30
提案日: 2008/10/11

人に仕事を頼む感覚でクラスを作る

概要

クラスを作るときに、人に仕事を頼む感覚を持つ、といったアイディアです。この感覚でクラスを作ると、どんなクラスを作ればよいかが自然に分かるようになります。具体的にでイメージできるようになり、クラスが意味的にきれいに分かれます。

背景

分離したものをクラスに渡す感覚

別ページ「クラスを作るタイミング」の中で、複雑と感じたタイミングでクラスに分離するアイディアについて記載しました。この中で分離について考えていると、分離したものを新しいクラスに渡す感覚が、まるでクラスに何かをお願いしているような感じがして、クラスにものを頼む感覚が生まれてきました。クラスにものを頼むと、クラスが人のように見えてくるので、人に仕事を頼む感覚でクラスを作る、といったアイディアに至りました。

この感覚が出てきた背景 : 複雑と感じたときにクラスへ分離していて→分離したものを別の新しいクラスに渡す感覚が、まるでクラスにお願いしているよう→クラスにものをお願いする様子から、クラスに頼む感じが生まれる。クラスに頼むので、クラスをまるで人のように考える感覚が出てくる。仕事と考える背景は?

どんなもの?

人に仕事を頼む感覚で作る

クラスを作るときに、人に仕事を頼む感覚を持つ、といったアイディアです。人に仕事を頼む感覚でクラスを作るとは、やりたい大きな仕事を単純な仕事へと分けて、仕事をクラスに割り振り、クラスを作る、といったことになります。人に仕事を頼む感覚を持つことで、単純な仕事へと分けるとき、人に頼むならどのように仕事を分ければよいか、具体的にでイメージできるようになり、仕事が意味的にきれいに分かれます。分けた仕事をクラスに割り振ればよいので、どんなクラスを作ればよいかが自然に分かるようになります。

ここでは人に仕事を頼む感覚で、実際にクラスを作ってみます。具体的な例に沿ってクラスを作った後、どんな考え方でクラスを作ればよいか、ポイントとなる点を表でまとめます。

例 : クラスに仕事を頼んでみる

まず、1つの大きな仕事を与えます。例えば、ユーザーがPCを使った時間をログして表示してほしい、といったものを考えます。この仕事を人に頼む感覚で、クラスに仕事を頼んでみます。

このPCの使用時間を表示する仕事は、大きな仕事ですべきことが多くありそうです。この仕事を人に頼むとすると、全部を1人に頼むより、何人かに頼んで分担させた方がよさそうな感じがします。同じように、クラス1つで対応しても仕事は達成可能ですが、より単純な仕事に分けられるなら、出来る限り単純な仕事まで分けて、複数のクラスに担当させます。人は何人も呼べませんがクラスはほぼ無限に作れるので、いくつまで分けても問題ありません。

大きな仕事を頼まれたとき、それをどれだけ単純な仕事に分けられるかが腕の見せ所になります。その仕事が持つ本質を客観的に捉えて、実際に何をすればよいかを考えます。いろいろ考えられる分け方の中から、最も単純な仕事に分けられるパターンを探します。

大きな仕事を複数に分けるときには、複数の人に役割分担をしてもらう感覚で仕事を分けます。大きな仕事を達成するには、どのようなことをしてくれる人がいればよいかを考えます。こんなことをしてくれる人がいればいいなあと感じたら、それが1つの役割の内容になります。

仕事を大きく3つに分ける

PCの使用時間を表示する例では、まず、右の図にあるように大きく3つに分けます。今ユーザーがPCを使っているかどうかを判断する仕事と、使用状況をファイルにログする仕事、ログ内容を読み込んでUIに表示する仕事の3つです。

3人呼んで3つの仕事を割り振る感覚で考えます。1人はユーザーの動きをずっと観察し続けて使用中かどうかを判断することに集中する人、1人はその判断を元にログを書き込むだけの人、もう1人はユーザーからログが見たいと言われたときにログを読み込んで表示する人の3人です。実世界で考えても、この仕事を1人で全部するより、この形で3つに分けて3人で仕事をした方が、各人の負担が減って楽そうです。

人に仕事を頼む感覚で3つの仕事を3つのクラスに割り振ります。仕事を与えられたクラスは、その仕事についてだけを考えればよく、他の仕事は他のクラスに任せます。クラスは与えられた仕事のみをキッチリと果たすようにします。割り振られた仕事だけを考えながら、その仕事のみをキッチリと果たすようにクラスの中身を実装する、といったことになります。実装では、その仕事を果たすことだけを考えればよいので、3つのクラスを別々に独立して実装することが出来ます。

3人の間にある関係

仕事を割り振った3つのクラスは他のクラスとイベントを使って連絡を取り合いながら、PCの使用時間を表示するといった1つの大きな仕事を達成します。そこで、3人が協調してこの仕事をするときに、どんな連絡を取ればよいのかを考えて、3人の間に必要な関係を明らかにしておきます。

PCの使用中を判断する人は、使用中と判断した瞬間に、この判断をログを書く人に伝えます。ログを書く人は判断が来たらログを書いて保存します。この人はただログを書くだけで、特に他の人に伝える必要はないことが分かります。UIに表示する人は、ユーザーからログが見たいと言われたときに、書かれたログを読み込んで表示します。クラスの間に必要なイベントは、使用中の判断をする人が使用中かどうか判断した瞬間にログを書く人に伝える所だけと分かります。

このように、人に仕事を頼む感覚でクラスを作った結果、明確な役割を持つ3つのクラスを作ればよいことが分かりました。それぞれのクラスは与えられた役割を果たすように実装します。クラスの間に必要なイベントも分かりました。クラスが持つべき最低限のインターフェースになります。このインターフェースを持つように実装します。

人に仕事を頼む感覚でクラスを作ると...

前述で挙げたような例から、人に仕事を頼む感覚でクラスを作ると、どのような良い点があるのかについて表にまとめます。

1. 具体的に人でイメージできるようになる
その仕事をもし人がするならどうするか、といったイメージで考えられます。まるで、クラス構成(仕事の役割分担)を考えるときは上司になり、1クラスを実装する(与えられた仕事をこなす)ときは部下になったように、具体的にイメージできるようになります。
2. 大きな仕事を分けようと思うようになる
大きな仕事は、1人に全部を頼むより、何人かに頼んで分担させた方がよさそうなことに気付きます。複数のクラスに仕事を分けようと思うようになります。
3. 仕事を意味的にきれいに分けられるようになる
人に役割分担する感覚で仕事を分けられます。実世界のイメージで役割を考えることで、仕事が意味的にきれいに分かれるようになります。
4. クラスが実装しやすくなる
単純な仕事へと分けてクラスに割り振るので、1つのクラスがすべき仕事が減り、果たすべき責任が分散されます。1クラスのすべき仕事が減ると、そのクラスを実装しやすくなります。
5. どんなクラスを作ればよいかが分かるようになる
仕事を人に割り振って、人をクラスにするだけなので、どのタイミングでどんなクラスを作ればよいかが自然に分かるようになります。考えの中から必要なクラスが自然に発生してきます。
6. クラスが協調するようになる
クラスを人と考えることで、クラスがより協調するように開発者の考えが働きやすくなります。クラスを協調させず、中央のクラスがすべてのクラスを操って処理を進めるより、クラスを協調させて、関係するクラスの間だけで出来る仕事は閉じるようになります。出来ることは任せてしまい、責任を委譲することで、責任が分散されてクラスが実装しやすくなります。
7. 他のクラスとの協調方法が分かるようになる
もし人が協調してその仕事をするなら、どんな連絡を取り合えばよいかをイメージすることで、クラスの間でいつどんな情報を伝える必要があるのかが分かるようになります。
8. クラスがアクティブに振る舞うようになる
クラスを人として考えるので、クラスが人のようにアクティブに振る舞うイメージで考えられます。人がグループの中でお互い協調して仕事をするように、クラスが協調して働くようになります。

 

最後に表でまとめ : 人に仕事を頼む感覚でクラスを作ると...1. その仕事を人がするならどうするかといったイメージで考えられる(クラス構成を考えるときは上司になり、1クラスを実装するときは部下になる)=具体的にイメージできるようになる、2. 大きな仕事を分けようと思うようになる、3. 人に役割分担する感覚で仕事を分ける=意味的にきれいに分けられるようになる、4. 仕事を人に割り振り、人をクラスにするだけ=どのタイミングでどんなクラスを作ればよいかが自然に分かるようになる=考えの中からクラスが自然に発生する、5. 人が連絡を取り合うイメージから、必要な協調方法が分かる

さらに細かく分ける

前述では、PCの使用時間を表示する大きな仕事を3つに分けて、3つのクラスに仕事を頼みました。しかし、実際にクラスを実装しようと考えると、3つのうち使用中を判断するクラスはさらに細かく分けた方がうまく仕事を達成できることが実装の途中で分かりました。ここではこのクラスを2つに分けます。

マウスとキーボードの2つのクラスとまとめ役を果たすクラス [全体]

使用中を判断する仕事を実際にクラスで行おうとすると、具体的にどのようにして使用中を判断するかを考える必要があります。ここでは使用中かどうかをマウスの動きとキーボードの入力によって判断し、入力が入り始めれば使用開始、ある一定時間以上入力がなければ使用終了、と判断を決めることにします。ここで、マウスとキーボードの仕事を2つに分けられるため、使用中を判断するクラスをマウスで判断するクラスとキーボードで判断するクラスの2つに分けます。

このとき、マウスとキーボードの2つのクラスがあるので、2つの判断結果が返ってきます。そこで、これらを統合して、最終的に使用中かどうかを判断するまとめ役が必要です。このまとめ役を果たすクラスをもう一つ追加します。

実際に実装しようと考えると、マウスとキーボードで判断する2つのクラスは内容が随分似ていることに気付きます。マウスかキーボードかといった入力が違うだけで、判断する部分はコードが同じになります。そこで、判断するという同じ機能を抽出して汎化したクラスを作ります。この汎化したクラスにマウスとキーボードの入力状態を伝えることで使用中を判断します。

汎化したクラスを作る [拡大] クラスは2つ、インスタンスは3つ [拡大]

汎化したクラスを作ったことで、マウスとキーボードで判断する2つのクラスは、両方この汎化クラスを使えばよくなり不要になります。すると必要なクラスは、汎化クラスとまとめ役のクラスの2つになりました。一方、実行時には汎化クラスを2つインスタンス化して、マウス用とキーボード用に使用します。まとめ役のクラスは1つインスタンス化して、汎化クラスの2つのインスタンスから出てくる判断を統合して最終的な判断を出します。

人クラス、アダプタクラス、汎化クラス

前述の使用中を判断するクラスを実装する中で、4つのクラスが出てきました。マウス、キーボードでそれぞれ使用中を判断する2クラスと、この2つをまとめるクラス、判断という同じ機能を汎化したクラスの4つです。これらのクラスがどのような性質を持っているかを考えてみます。

これまで人に仕事を頼む感覚でクラスを作ると、クラスはまるで人のように振る舞い、クラスを人として考えてきました。前述の4つのクラスはすべてこの人クラスの性質を持っています。マウス、キーボードから使用中を判断する人、結果をまとめる人、汎化クラスも任意の入力によって使用中を判断する人、といったように仕事を果たす人として考えました。

クラスは純粋な人クラスになる一方で、人クラス以外に他の性質を持つことがあります。結果をまとめるクラスは、次に待つログするクラスに判断結果を送るため、2つある結果を1つにまとめます。このようにこのクラスは、インターフェースの違いを吸収したり、機能を集約したりする性質を持っています。このクラスをアダプタクラスと呼ぶことにします。アダプタクラスはその機能に必要なメインの役割ではなく、2つの間のインターフェースを調整する補助的な役割を果たします。

汎化クラスは、マウスやキーボードなどの入力の違いを汎化して、任意の入力によってPCを使用中かどうか判断します。このように汎化クラスは、機能を汎化するために作られる性質を持っています。コードの中には、純粋な人クラスだけではなく、他の性質を持つクラスもあることが分かります。

協調はイベントで。さらに、判断を4クラス、ログを2クラス。
判断 : マウス、キーボードの2クラスへ分ける=人クラス、2つをまとめるクラス(インターフェースの違いを吸収 or 機能の集約...)=アダプタクラス、判断という同じ機能を汎化したクラス=汎化クラス。
人として仕事をするように振舞う人クラスの他にも、2つの間のインターフェースを調整するアダプタクラス(アプリのメインの実装ではない)、機能を汎化するために作る汎化クラスの存在があることに気付く。人クラスがすべてではない。
汎化によって、マウス、キーボードの2クラスは汎化クラスのインスタンスを使えばよくなり必要なくなる→クラスは汎化&アダプタの2クラス、インスタンスとしては汎化2つ&アダプタ1つの3オブジェクトが実行時にロードされることになる。

結局、人に仕事を頼む感覚でクラスを作るとは?

人に仕事を頼む感覚でクラスを作るとは、仕事といった感覚を持ち込んで、実現したい機能を1つの仕事と考えます。そして、この仕事を複数のクラスで分担できるように分割します。仕事を分割したら、それぞれをクラスに割り振ります。クラスに仕事を割り振って、仕事をクラスに任せます。実際にクラスの中身を実装するときには、この与えられた仕事の範囲をキッチリ果たすように実装します。

クラスの中身を実装するときに、割り振られた仕事がまだまだ分割できることに気付くことがあります。仕事は分割すればするほど単純になりクラスで扱いやすくなります。なので、気付いたときには随時仕事を分割して、再度、クラスに割り振ります。このように、仕事を分割して割り振り、割り振った仕事をさらに分割して割り振り、を繰り返して、仕事をなるべく単純にします。

結局のところ、人に仕事を頼む感覚でクラスを作るとは、実現したい機能を仕事と考えて、仕事を分割してクラスに割り振り、クラスを実装する、といったことになります。

別記事 : 状態が2つ以上あるとクラスは難しくなる
ログする仕事を分ける
[絵]ログする仕事を実際にクラスで行おうとすると、具体的にどのようにしてログするかを考える必要があります。前述した判断結果をまとめるクラスから、PCの使用開始時刻と終了時刻が別々のタイミングで伝えられます。ここではこの開始と終了時刻を1セットにして.csv形式のファイルに書き足すことでログします。
さらにログできない場合があると困るので、PCが休止状態に入っても、アプリケーションが強制終了しても、必ずログできるように、ログの書き出し方を工夫します。工夫は、書き込む予定の開始と終了時刻のセットを、毎分ごとに別のファイルへ保存します。このとき終了時刻には現在の時刻を常に入れて保存します。すると、強制終了してログできなかった場合は、毎分ごとに保存したこのファイルから開始と終了時刻を読み出すことで、ログ予定だった情報を復元でき、次回アプリケーションを実行したときに、復元したログを保存します。
このように確実にログするため少し複雑なこの仕事を1つのクラスで実装しようとすると、クラスが難しくなります。開始と終了時刻が伝えられるタイミングや、毎分ごとに保存するタイミングなど、いくつものタイミングを管理する必要があります。1つのクラスの中で管理する状態が2つ以上あると、そのクラスは一気に難しくなる傾向があります。クラスのコードはいかにも特殊なことをする理解しにくいコードになります。
お互いに関係のある「状態」ならいくつあっても難しくはならないが、関係のない状態が2つ以上あると難しくなる。それはクラス内のコードでこの部分はどちらの状態管理に関係するコードなのかが分からなくなり、読みにくくなるから。理解するには管理の仕組みをすべて知る必要がある。では、どちらの状態管理かを示すために、コードの各部にコメントを書いてどちらかを示しておけばよい? それをするなら、(それができるぐらい明確に分けられるなら)、別クラスへ分けてしまえばよい。1クラスで1状態を管理する。とは言っても、クラス間でデータの受け渡しを頻繁にする必要があり、やはり1つのクラスで処理した方がよかったのでは? この点はイベントを使って伝達し、必要なイベントが5~10個と多くなっても構わない。クラスへ分けることで得られるメリットの方が大きいとする。

世の中の似た機能

ブログなどでクラスの動きを説明するときに、クラスを小人と呼んで説明する記事があります。クラスを小人と擬人化して考えて、クラスがアクティブに働く様子を考えています。

また、オブジェクト指向に関する書籍の中で、オブジェクト指向を使ってプログラムを作る感覚は擬人化役割分担になる、といった説明があります。このようにオブジェクトを擬人化することが多くあります。

クラスをコビトと表現されたり=クラスの擬人化。

 

更新履歴
2009/10/07
  • 書き始め
2009/12/10 v1
  • 初版作成
2009/12/13 v2
  • 図を作成
2009/12/20 v3
  • 「結局、人に仕事を頼む感覚でクラスを作るとは?」を追加

※ご意見、ご感想、改善点、その他の情報などがありましたら、メールにてお知らせ願います。

Copyright (C) 2002 - 2019 Simon.P.G. All Rights Reserved. Top | Simon.P.G. とは | 使用条件 | ご意見
inserted by FC2 system