とあるシステム会社にて
ワイ「なあ、ハスケル子ちゃん」
ワイ「いま開発してるWebアプリケーションについて、相談したいことがあるんやけど」
ハスケル子「はい」
ハスケル子「どんな内容ですか?」
ワイ「あのな」
ワイ「色んなページで、配列をソートして表示する必要があるんやけど」
ワイ「そのソート内容がちょっと複雑やねん」
ワイ「なんかオススメのライブラリある?」
ハスケル子「LodashのorderByなんて便利ですよ」
ワイ「マジかいな」
ワイ「さっそく使ってみるわ」
##ライブラリを導入してみる
ワイ「まずは・・・」
npm install lodash.orderby
ワイ「↑こう、npm
でインストールして」
import lodashOrderBy from 'lodash.orderby'
ワイ「↑こう、import
してやれば使えるな」
ワイ「さて、このライブラリはどんな風に使うんかな?」
ハスケル子「ええと、例えば・・・」
- ユーザーの情報が格納された
userList
という配列をソートする - 名前の昇順でソートする
- 同姓同名の場合は、年齢の降順でソートする
ハスケル子「・・・なんてことがしたい場合は」
const sortedUserList = lodashOrderBy(userList, ['name', 'age'], ['asc', 'desc'])
ハスケル子「↑こんな風に書きます」
ワイ「なるほどな」
ワイ「第一引数には、ソートしたい配列を」
ワイ「第二引数には、ソートするプロパティ名を」
ワイ「第三引数には、昇順 or 降順を」
ワイ「それぞれ指定すればええんやな!」
ハスケル子「はい」
ワイ「よっしゃ、この関数、いろんなページで使わしてもらうわ!」
ハスケル子「待ってください、やめ太郎さん」
ワイ「ん、なに?」
ハスケル子「lodash.orderby
のラッパー関数を作って」
ハスケル子「各ページからは、そのラッパー関数を呼び出すようにした方がいいですよ」
ワイ「ラッパー・・・?」
ワイ「何なんそれ」
ハスケル子「lodash.orderby
をラップした関数を、自分で書くってことですよ」
ワイ「え、なんで?」
ハスケル子「その方が、保守性が上がります」
ハスケル子「具体的には、将来的にそのライブラリを別のものに入れ替える場合とかに」
ハスケル子「だいぶ手間が省けます」
ワイ「(何を言うてるか分からんけど・・・)」
ワイ「よっしゃ、分かった!やってみるわ!」
ライブラリをラップする、とは
ワイ「何をすればええねや・・・?」
ワイ「ラッパー関数を書けって言うてたな」
ワイ「・・・ということは・・・」
// lodash.orderby を import
import lodashOrderBy from 'lodash.orderby'
// lodash.orderby をラップした関数
export const orderBy = (itemsList, sortKeys, sortOrders) => {
return lodashOrderBy(itemsList, sortKeys, sortOrders)
}
ワイ「↑こんな感じか・・・?」
ワイ「いや、意味なさすぎるやろ」
ワイ「だって、このorderBy
関数」
ワイ「受け取った引数をlodashOrderBy
にそのまま渡してるだけやもん」
ワイ「これは多分ちがうな」
ワイ「ほなラッパー関数って、何のことやろ・・・」
ワイ「ラッパー、ラッパー・・・」
ワイ「・・・あっ!!」
分かった
ワイ「ハスケル子ちゃん、さっそくライブラリをラップしてみたで!」
ワイ「聞いてみてくれや!」
ハスケル子「は、はい」
ハスケル子「(聞くとは・・・?)」
ワイ「行くで!」
誰かが作ったライブラリ
ラップするだけでだいぶマシ
入れ替える手間ほぼ皆無だし
マジ教えてくれたアイツ神
ワイ「どや?」
ハスケル子「逆に、どう思います?」
ハスケル子「これをどうプログラムに組み込むんですか?」
ワイ「そ、それな」
ハスケル子「そうじゃなくて」
ハスケル子「最初に書いてた関数の方で合ってますよ」
ワイ「ファッ!?」
// lodash.orderby をラップした関数
export const orderBy = (itemsList, sortKeys, sortOrders) => {
return lodashOrderBy(itemsList, sortKeys, sortOrders)
}
ワイ「↑これかいな!?」
ワイ「なんでこんな、意味のないorderBy
関数を作るん?」
ハスケル子「意味ありますよ」
ハスケル子「例えば、ライブラリをラップしないで」
ハスケル子「いろんなページから直接lodashOrderBy
を呼び出している場合だと」
ハスケル子「将来的にライブラリを入れ替える場合とかに面倒じゃないですか」
ワイ「まあ、ちょっとだけ面倒やな」
ワイ「いろんなファイルのlodashOrderBy
を」
ワイ「xxxSortLibrary
とかに検索置換せなあかんもんな」
ワイ「でも大した手間やないやん?」
ハスケル子「検索置換だけじゃ済まない場合もありますよ」
ハスケル子「例えば・・・」
// 新しく使う xxxSortLibrary というライブラリは
// 引数の渡し方が lodash.orderby と違う
// (オブジェクトとして渡さないといけない)
const sortedUserList = xxxSortLibrary({
array: userList,
sortKeys: ['name', 'age'],
sortOrders: ['asc', 'desc']
})
ハスケル子「↑こんな感じで」
ハスケル子「似たようなライブラリでも、引数の渡し方が結構ちがうこともあるじゃないですか」
ワイ「ほんまや・・・」
ワイ「こんなん、いろんなページで修正するとなったら、結構めんどくさいで・・・」
ハスケル子「ですよね?」
ラッパー関数を作っていた場合
ハスケル子「ラッパー関数を作って、それを使っていた場合は」
ハスケル子「1箇所だけの修正で済みます」
// xxxSortLibrary を import
import xxxSortLibrary from 'xxxSortLibrary'
// xxxSortLibrary をラップした関数
export const orderBy = (itemsList, sortKeys, sortOrders) => {
return xxxSortLibrary({
array: itemsList,
sortKeys,
sortOrders
})
}
ハスケル子「↑こんな感じで、ラッパー関数を修正してあげるだけです」
ワイ「なるほどなぁ」
ワイ「自作のラッパー関数が受け取った引数たちを」
ワイ「今まではそのまま渡すだけやったけど」
ワイ「新しいライブラリ関数に合う形式に変換して、渡してやればええわけか」
ハスケル子「そうです」
ハスケル子「ラッパー関数が作ってあれば」
ハスケル子「ライブラリを入れ替えた場合でも」
ハスケル子「各ページからの呼び出し方は変わりません」
// ラッパー関数の引数は変わっていないので
// 各ページからは今まで通り使える
const sortedUserList = orderBy(userList, ['name', 'age'], ['asc', 'desc'])
ワイ「おお、ほんまや・・・」
ワイ「呼び出し側はそのままでOKなんやね」
ハスケル子「はい」
ハスケル子「それに、ライブラリを入れ替える場合だけじゃなく」
ハスケル子「ライブラリのバージョンアップによって、インターフェース(使い方)が変わる可能性もありますし」
ワイ「なるほどな」
ワイ「最近のフロントエンド開発では、多数のnpmライブラリを使うし」
ワイ「流行り廃りが激しいから、ライブラリを入れ替えることもあるもんな」
ワイ「メンテナンスコストのことも考えとかんとな」
まとめ
- ライブラリの関数をラップした関数を作っておくと、あとあと便利
- 逆に、ラップしないで直接呼び出していると、ライブラリの入れ替え時に手間がかかる
- 新旧ライブラリの**インターフェースの違い(使い方の違い)**を、ラッパー関数が吸収してくれる
- 結果的に保守性が上がる
- 関数名を
iLoveHipHop()
にするとか、そういうことではない
ワイ「ってことやな!」
ハスケル子「ですね!」
〜おしまい〜
新しい記事もよろしくやで
スペシャルサンクス
ばりとんさん(考え方を教えてもらったから)
きんみさん(ラップをパクらせてもらったから)
追記
ばりとんさんも記事を書いてたから、被りまくってしまった。
ばりとんさん、申し訳ありません・・・!