とあるWeb制作会社にて
上司くん「なあ、やめ太郎くん。Aくんが作ったこのページにハンバーガーメニュー付けてぇや」
ワイ「はい!よろこんで!(Aくんにやらせろや、ダボが!)」
とりあえず作ってみる
ワイ「メニュー部分のタグにはmenuっちゅうIDが付いとるから・・・」
ワイ「その要素を取ってきて定数menuに格納するんや。」
const menu = document.getElementById("menu");
//menuというIDを持つ要素を取ってきて定数menuに格納 。
ワイ「ハンバーガーボタンも定数に入れとこ」
const hamburgerBtn = document.getElementById("hamburgerBtn");
//hamburgerBtnというIDを持つ要素を取ってきて定数hamburgerBtnに格納。
ワイ「初期状態ではメニューは非表示やな」
menu.style.display = "none";
//初期状態ではメニューを非表示に。
ワイ「ハンバーガーボタンをクリックしたら、メニュー開閉用の関数が実行されればええねん」
hamburgerBtn.addEventListener("click", hideAndShow);
//ハンバーガーボタンをクリックしたらメニューの表示非表示を切り替える関数が実行されるようセット。
ワイ「メニュー開閉用の関数はこないな感じやな。閉じとったら開く、開いとったら閉じる。」
//メニューの表示非表示を切り替える関数
function hideAndShow(){
if(menu.style.display === "none"){
//現在メニューが非表示なら表示させる。
menu.style.display = "block";
}else{
//現在メニューが表示されているなら非表示に。
menu.style.display = "none";
}
}
ワイ「・・・動きよったわ!完成や!」
const menu = document.getElementById("menu");
//menuというIDを持つ要素を取ってきて定数menuに格納 。
const hamburgerBtn = document.getElementById("hamburgerBtn");
//hamburgerBtnというIDを持つ要素を取ってきて定数hamburgerBtnに格納。
menu.style.display = "none";
//初期状態ではメニューを非表示に。
hamburgerBtn.addEventListener("click", hideAndShow);
//ハンバーガーボタンをクリックしたらメニューの表示非表示を切り替える関数が実行されるようセット。
//メニューの表示非表示を切り替える関数
function hideAndShow(){
if(menu.style.display === "none"){
//現在メニューが非表示なら表示させる。
menu.style.display = "block";
}else{
//現在メニューが表示されているなら非表示に。
menu.style.display = "none";
}
}
#上司くんに提出してみる
ワイ「できました!ユーザビリティを考慮した結果、敢えてアニメーションとか付けずにシンプルな実装になってます〜」
上司くん「ええで、ええで!こないな感じや」
ワイ「おおきに、ほな失礼します」
時は経ち、上司くんがナイスな追加仕様を思いつく
〜1ヶ月後〜
上司くん「やめ太郎くん、すまん」
ワイ「えっ?」
上司くん「画面の右上のハンバーガーボタンだけやなく、メニュー自体の一番下のとこにも閉じるボタン付けてや」
ワイ「ええですよ〜(前の時に言えや、ボケが!)」
#先月書いたJSを修正していく
ワイ「先月書いたコードなんて忘れてもうたわ・・・3秒経ったら他人のコードいうやつや・・・まぁ何とかなるやろ」
ワイ「要はハンバーガーボタンのほかに、閉じるボタンがクリックされた時もメニューが開閉すればええねん」
ワイ「閉じるボタンにはcloseBtnいうIDを付けといたから・・・こんな感じや。」
/* 追加コード ここから */
const closeBtn = document.getElementById("closeBtn");
//closeBtnというIDを持つ要素を取ってきて定数closeBtnに格納。
ワイ「そんでそのボタンをクリックした時にもメニュー開閉関数が実行されんねん」
ワイ「メニュー開閉関数はどれやったっけ・・・あぁ、これや、hideAndShowや」
closeBtn.addEventListener("click", hideAndShow);
//閉じるボタンをクリックした時にもメニューの表示非表示を切り替える関数が実行されるようにセット。
/* 追加コード ここまで*/
ワイ「完成や」
const menu = document.getElementById("menu");
//menuというIDを持つ要素を取ってきて定数menuに格納 。
const hamburgerBtn = document.getElementById("hamburgerBtn");
//hamburgerBtnというIDを持つ要素を取ってきて定数hamburgerBtnに格納。
menu.style.display = "none";
//初期状態ではメニューを非表示に。
hamburgerBtn.addEventListener("click", hideAndShow);
//ハンバーガーボタンをクリックしたらメニューの表示非表示を切り替える関数が実行されるようセット。
//メニューの表示非表示を切り替える関数
function hideAndShow(){
if(menu.style.display === "none"){
//現在メニューが非表示なら表示させる。
menu.style.display = "block";
}else{
//現在メニューが表示されているなら非表示に。
menu.style.display = "none";
}
}
/* 追加コード ここから */
const closeBtn = document.getElementById("closeBtn");
//closeBtnというIDを持つ要素を取ってきて定数closeBtnに格納。
closeBtn.addEventListener("click", hideAndShow);
//閉じるボタンをクリックした時にもメニューの表示非表示を切り替える関数が実行されるようにセット。
/* 追加コード ここまで*/
ワイ「でも、なんか、あんま読みやすくないな、ワイのコード・・・」
ワイ「1ヶ月も経つと、どの変数に何が入ってて、どの関数が何のための機能なのか忘れてまうわ」
ワイ「コメントがあるから何とかなるけども・・・」
ワイ「先輩方のコードはもっと読みやすかったような・・・」
ワイ「たしかオブジェクト指向いうやつで書いてはんねん、あの人らは・・・」
ワイ「オブジェクト指向で書けば読みやすくなるんかな・・・」
オブジェクト指向で書き直してみる
ワイ「よっしゃ、ワイもいっちょオブジェクト指向いうやつで書いてみるで」
ワイ「オブジェクト指向いうんは、たしかオブジェクト同士がメッセージングし合って仕事をしてくれるらしいねん・・・どんな状況やねんそれ」
ワイ「よう分からんけど、まずはオブジェクトを生成するための設計書みたいなclassいうんを書くんや」
classを書いてみる
ワイ「ハンバーガーメニュー設定くんちゅう装置を作って、そいつに指示するイメージで考えてみよか」
ワイ「ほな、クラスの名前はHamburgerMenuSetteikun(ハンバーガーメニュー設定くん)や」
//ハンバーガーメニュー設定くん(以下、設定くん)の設計図となるクラス
class HamburgerMenuSetteikun{
}
ワイ「それをnewして設定くん1号を生成するんや」
//ハンバーガーメニュー設定くんを実際に生成して、設定くん1号と名づける。
const setteikun1gou = new HamburgerMenuSetteikun("menu");
ワイ「ほんでその子に指示をするんや」
ワイ「設定くんは、ハンバーガーボタン設定メソッドを持ってんねや。それを実行せえ、と。」
//設定くん1号の、ハンバーガーボタン設定メソッドを実行。IDとして「hamburgerBtn」を指定。
setteikun1gou.setBtn("hamburgerBtn");
ワイ「やりたい事はこんな感じや。」
ワイ「ほなこれを実現するためにclassの方に戻るで」
classの中にコンストラクタを書く
ワイ「constructorいう名前のメソッド・・・まぁ設定くんが持ってる関数みたいなもんやな」
ワイ「設定くんが生成される時、最初にこのconstructorメソッドが実行されんねん」
//コンストラクタ・・・設定くんが作られたとき、最初に実行される処理。
//プロパティはコンストラクタの中で設定します。
constructor(menuId){ //menuIdには"menu"という文字列が入ってくる。
//thisは作成された設定くんを指します。
//この設定くんにとってのメニューとは、menuというIDを持った要素だよ、と設定。
this.menu = document.getElementById(menuId);
//初期状態ではメニューを非表示に。
this.menu.style.display = "none";
}
classの中にメソッドを定義していく
ワイ「設定くんが持ってる技をclassの中に書くねん」
ワイ「今回、技は基本的に1個だけや。メニュー開閉ボタンを設定してくれるsetBtnや。」
ワイ「引数で指定されたボタンがクリックされたら、メニューが開閉するように設定する技や」
//ハンバーガーボタンを設定するメソッド
setBtn(btnId){
//btnId(つまりhamburgerBtn)というIDを持つ要素を定数btnに格納。
const btn = document.getElementById(btnId);
//hamburgerBtnがクリックされたら、
btn.addEventListener("click", ()=>{
//メニューの表示非表示を切り替えるメソッドを実行。
this.hideAndShow();
});
}
ワイ「実際に開閉する部分の処理はやっぱり別メソッドにしておこか。」
//メニューの表示非表示を切り替えるメソッド
hideAndShow(){
if(this.menu.style.display === "none"){
//現在メニューが非表示なら表示させる。
this.menu.style.display = "block";
}else{
//現在メニューが表示されているなら非表示に。
this.menu.style.display = "none";
}
}
ワイ「できたで!」
//ハンバーガーメニュー設定くん(以下、設定くん)の設計図となるクラス
class HamburgerMenuSetteikun{
//コンストラクタ・・・設定くんが作られたとき、最初に実行する処理。
//プロパティはコンストラクタの中で設定します。
constructor(menuId){ //menuIdには"menu"という文字列が入ってくる。
//thisは作成された設定くんを指します。
//この設定くんにとってのメニューとは、menuというIDを持った要素だよ、と設定。
this.menu = document.getElementById(menuId);
//初期状態ではメニューを非表示に。
this.menu.style.display = "none";
}
//ハンバーガーボタンを設定するメソッド
setBtn(btnId){
//btnId(つまりhamburgerBtn)というIDを持つ要素を定数btnに格納。
const btn = document.getElementById(btnId);
//hamburgerBtnがクリックされたら、
btn.addEventListener("click", ()=>{
//メニューの表示非表示を切り替えるメソッドを実行。
this.hideAndShow();
});
}
//メニューの表示非表示を切り替えるメソッド
hideAndShow(){
if(this.menu.style.display === "none"){
//現在メニューが非表示なら表示させる。
this.menu.style.display = "block";
}else{
//現在メニューが表示されているなら非表示に。
this.menu.style.display = "none";
}
}
}
//ハンバーガーメニュー設定くんを実際に生成して、設定くん1号と名づける。
const setteikun1gou = new HamburgerMenuSetteikun("menu");
//設定くん1号の、ハンバーガーボタン設定メソッドを実行。IDとして「hamburgerBtn」を指定。
setteikun1gou.setBtn("hamburgerBtn");
ワイ「なんや、前のコードより長くなってもうてるやん・・・」
読んでみる
ワイ「コードの量は増えてもうたけど、設定くん関連の処理がclassの中にまとまってるから、どこからどこまでが設定くん関連のコードなのか分かりやすいな」
ワイ「あと、関数いうかメソッドも全部classの中に書いてあるからこのメソッドたちは設定くんのためのものいうのも一目瞭然やな」
ワイ「constructorいうのも名前が決まってるし、設定くんが最初に実行する処理やいうのも一目瞭然や」
ワイ「前のコードん時はhideAndShowが設定くんのための関数いう感じがせぇへんかったもんな」
ワイ「前のコードでメニュー開閉ボタンを設定する時は・・・」
hamburgerBtn.addEventListener("click", hideAndShow);
//ハンバーガーボタンをクリックしたらメニューの表示非表示を切り替える関数が実行されるようセット。
ワイ「・・・こないな感じで、別に分かりにくくはなかったけど、オブジェクト指向の方は・・・」
//設定くん1号の、ハンバーガーボタン設定メソッドを実行。IDとして「hamburgerBtn」を指定。
setteikun1gou.setBtn("hamburgerBtn");
ワイ「設定くん1号いうのが頭に付いてるから、設定くんが自分のメソッドを実行してる感が出てんねん」
ワイ「閉じるボタンを追加する時もこれだけやで」
//ボタンを追加したい時は、IDを変えて再度ボタン設定メソッドを実行。
setteikun1gou.setBtn("closeBtn");
ワイ「なんや、オブジェクト指向で書いた方がコンピュータにやらせたい事とコードの見た目が近くなるから、ワイのようなザコーダーでも読みやすいコードになるな」
ワイ「ヒューマンリーダブルなコードいうやつや」
オブジェクト指向っぽくやってみた感想
ワイ「なんや、オブジェクト指向いうんは大規模開発向けやと思っとったけど、小規模なコードでも意外といい感じやないか」
ワイ「メソッドは各クラスの中に書いてあるから、その装置の持つ機能やって分かりやすいしな」
ワイ「プロパティはそのオブジェクトの状態とか情報を入れとくのに使えるな」
ワイ「ただ今回のような小さいプログラムなら普通に処理や関数を書き連ねたほうが普通に速いな。手続き型いうやつやな。」
ワイ「こんなページ、どうせ今後たいした修正や運用もないしな。1年後にはまたデザイン変えんねん。」
ワイ「もっと大規模な開発の場合とかは、いろんな装置たちを並べてそいつらに仕事をさせるイメージでプログラミングできるから、分かりやすそうやな」
例えば・・・
ワイ「人間クラスを継承したDQNクラスから生成したたかしオブジェクトが、強盗メソッドを実行すんねん。」
ワイ「それを受けて銀行オブジェクトが出金メソッドを実行すんねやな。」
ワイ「顧客オブジェクトの残高プロパティは0や。」
ワイ「ほんならたかしオブジェクトは返り値として受け取った現金をタクシーの運ちゃんオブジェクトに渡して、逃走メソッドを実行すんねやな。」
ワイ「たかしの前科プロパティがまた1増えるんや」
ワイ「それがきっとオブジェクト同士がメッセージングし合って仕事をしてくれるいうやつなんやろな」
ワイ「仕事いうか犯罪やけどな」
ワイ「オブジェクト指向犯罪や」
まとめ
ワイ「普通に処理や関数を書き連ねる場合(手続き型)は、自分自身が呪文を唱えて戦ってた感じやったけど、オブジェクト指向の場合はポケモンか何かを生み出して、そいつらに指示を出してる感じやな」
ワイ「最初にそのポケモンたちの仕様を考えるのが面倒やけど、ポケモンを生み出したあとはその子らが戦ってくれる感じやから、監督業に専念できる感じでちょっと楽やねん」