初めに
皆さんはプログラミングを学習して行くうえで参考ソースや完成されたソースを見る際に「なぜこんな設計がおもいつくの?」と思ったことはないでしょうか?
私自身これがとてもありこんなきれいな設計・コーディング自分では思いつかないなコードを見ながら感じることが多いです。
今回はオブジェクト指向プログラミングにおいて良い設計・コーディングをするために必要な考え方を共有します。
1.責務の分離(Separation of Concerns)
責務の分離というのはざっくりいうと「1つのクラスが1つの役割だけを持つべき」という考え方。
下記のコードを見てみてください。
class Food{
constructor(public element: HTMLDivElement) {
element.addEventListener('click',this.clickEventHandler.bind(this));
}
clickEventHandler() {
this.element.classList.toggle('food--active');
}
}
class Foods{
elements = document.querySelectorAll<HTMLDivElement>('.food');
constructor() {
this.elements.forEach(element => {
new Food(element);
})
}
}
const foods = new Foods();
このコードはFoodsクラスでfoodを持つ要素をすべて取得しそれを1つずつコンストラクタ関数でFoodクラスもコンストラクタに渡しFoodクラス内でクリックイベントリスナを設定している。
これをもしFoodsクラスの中でfoodごとの処理まで全部やろうとすると、コードが複雑になる
なので、以下のようにクラスごとに役割を分けるのがポイント
・Foodクラス →1つのfood要素に対し処理を実行
・Foodsクラス →すべてのfoodをまとめて管理する
こうすることで管理がしやすくなり可読性も向上する
2.カプセル化(Encapsulation)
カプセル化をざっくりいうと「オブジェクトの内部の動作は、外部から直せず触れさせず隠蔽する」という考え方
先ほどのコードの一部を編集し例にします
class Food{
constructor(public element: HTMLDivElement) {
element.addEventListener('click',this.clickEventHandler.bind(this));
}
private clickEventHandler() {
this.element.classList.toggle('food--active');
}
}
このコードはclickEventHandler()をprivateにし外部から呼べないようにしています。
こうすることでクリック時の動作はFoodクラスだけで完結するようになります。
各クラスの中で完結するべき処理は外部に見せないようにするのがポイントになります。
では、こういう設計を思いつくにはどうすればいいか?
この設計を思いつくには3つの視点が大事
1.物事を「クラス単位」で考える習慣
・「1つのクラス = 1つの役割」を意識する
・何かのシステムや現実世界でどうクラス分けしたら整理しやすいかを考える
2.この処理はどのクラスがするべきか?を考える
・この処理はAクラスがやるべきかBクラスがやるべきかを考えると、自然と役割がわかれていく
・1つのクラスに責務を持ちすぎていないか?を考える
3.再利用しやすい設計か?を考える
・1つのクラスに処理を多く書くと汎用性がなくなる
・クラス分けを的確にして行くことで他の機能も流用しやすくなる