みなさんごきげんよう、8/11に最新JavaScript開発~ES2017対応モダンプログラミングという本が発売になります。技術書典2で好評だったModern JavaScriptという同人誌を加筆・修正・校正したもので、Impress R&D社さまのNextPublishing(ネクストパブリッシング)というところから出ます。ここはマストドンの書籍をいち早く出版した事で有名ですね!
この本の魅力は、なんと言ってもES2017のモダンな言語仕様、ユニットテスト、型といったイマドキのちゃんとしたプログラミングに必須な要素を濃縮した点です。「JSって言語仕様が変だよね」「プロトタイピングベースのOOPって何?気持ち悪い」とかそういう誤解を未だにしている人達に是非読んでもらいたいです!ES2015以後のJavaScriptはとても素直で気持ちよくプログラミング出来るようになっています!目次はこんな感じです。
さて本文なのですが、今日はJavaScriptに抽象化がもっと必要なんだという主張をさせていただきます。以前書いたJavaScriptにフレームワークが必要な理由という記事はまぁ元々は、React.js界隈の人に聞きたいという記事に対するアンサーとして、なぜReact.jsとかが必要なのか書いたものでした。
- 単純なペライチならjQueryでもなんでもいいんでは?
- トランスパイラやpolyfillは、古いモノは全部機械的に処理・吸収させればいいというアイデア及びASTをホゲるのが当たり前なので、JS文化圏においてはJSXやAltJSみたいなものがカジュアルに存在する
- Reactは、ベタにDOMを触るのではなく、低コストで抽象化できますよ
- SPAは、むしろサーバー側とクライアント側の開発コストの高騰を防ぐ一つの方策です
というものでした。自分の中で少し色々と整理しなおしたので、今回はこの中でもReactが低コストで抽象化できる話を掘り下げたいのですが、その前になぜ抽象化が必要かを先に書きます。
具象的すぎる世界に抽象化という秩序を与える
ソフトウェアという形が無いものにおいて抽象の反対は、具体と言うよりも具象だろ?と思ったのでここでは具象という言葉でいきます。
ちょっとしたスクリプトを20行程度書くのであれば、具象的なベタなコードを書いても誰にも文句を言われないでしょう。パスの決め打ち、マジックナンバーの頻出、あるいはミドルウェアをどういう叩き方をしたとしても、それが手っ取り早いのであればおそらくそれが正しい書き方です。YAGNI原則なんかも思い出してもいいでしょう。
ところがちゃんとした開発をするとなると具象的なコードばかりだと、他のチームメンバーや未来にメンテナンスする人(半年後の自分かもしれません)が困ります。具象的なコードが羅列されていると、読み解く為に様々な資料が必須になってしまいますし、それらはきっと分散していることも多いでしょう。
構造化、オブジェクト指向、デザインパターン、分割統治の原則、それらは密結合したものをほぐして、具象的すぎるコードに、抽象化という秩序を与えるもので、プログラミングの歴史で発達したものです。ミドルウェアにアクセスするためにわざわざレジスタだのDMAだの叩きますか?あるいはSocket通信でダイレクトにパケットをやりとりしますか?それよりは、なんらかのドライバーを使うでしょう。もっと言えば、個々のドライバーの仕様なんて考えずに統一・抽象化された扱いやすいAPIを使いたいですよね。本来、集中したいのはドメインロジック(この場合、解決したい問題の本質部分)であって、細かい下のレイヤーの話なんてほんとはどうでもいいはずです。
ただし現状では様々な制約条件のせいで、ある程度細かい下のレイヤーを意識しなければなりません。でもこの時、密結合をしている理由はないはずです。クヌース先生のいうところの「早すぎる最適化」の罠に引っかかってはいけません。ちょっとした変更が大きい部分に波及しやすくメンテナンスコストが増えるだけです。せめてドメインロジックと下のレイヤーは切り分けて考えましょう。たとえばDDDで言うインフラ層です。ドメイン層では、ストレージへのアクセスは抽象化したもの(インターフェースでもいいしファサードでもいいです)を使い、インフラ層のコードを何らかの形でインジェクションなりすればいいのです。設定のたぐいもダイレクトに記述せず何らかの形で外に逃がしましょう。ものによってはリポジトリに入れると事故の元なので、direnvとかを駆使するのはとても良い手です。
とは言っても、抽象化をやり過ぎると、FizzBuzz Enterprise Editionのようになってしまいます。YAGNI原則を覚えておくべきです。抽象と具象を行き来しうまくバランスを取るというのがとても大切です。うまく抽象と具象を分けましょう。デザインパターンはしかも、早めに罹患してすぐ治しましょう。
Reactは低コストでDOMという具象的なものから離れる事ができるもの
抽象化は、大昔の感覚で言えばコストがかかるものでした。なぜ老害な人達が早すぎる最適化をしたがっていたのか、それはある種のプログラミングに対して心がMOTTAINAIと叫ぶからなのです。抽象化という余計なモノを挟むことでスピードが遅くなったりメモリ喰うじゃないか!と昔の人は真顔で主張していたのです。富豪的プログラミングという言葉が言われるようになってから長い時間が経ちました。おそらく現代に「抽象化なんて重い」と言い出す人はそんなに生き残ってはいないでしょう。アセンブラ上等と言ってるようななんとか3zさんとかでもさすがに「抽象化は重い」とかは言い出さないですよね・・・?
ReactではComponentが命です。むしろComponentを作らないのであればReactの価値はありません。ではここでいうComponentはどうあるべきか。それは先ほどから言ってる抽象化です。仮想DOMっぽい技術によって低コストでComponentの分割という抽象化が実現できてしまうので、それを利用しない手はありません。
import Header from './header'
import Footer from './footer'
import Content from './content'
import Menu from './menu'
export default const PageApp = baseStyle => <div>
<Header baseStyle={baseStyle} .... />
<Menu baseStyle={baseStyle} .... />
<Content baseStyle={baseStyle} .... />
<Footer baseStyle={baseStyle} .... />
</div>
たとえば、パーツをHeader, Footer, Content, Menuに分けます。具体的な中身はその先で定義するわけですが、ここの時点で、たとえば「マテリアルUI風のスタイルで」というような情報を何らかの形でインジェクションしてもいいわけです。これの場合だとbaseStyleは、そういったカスタマイズするための情報です。CSSをwebpackで取り込んでもいいですし、何らかのオブジェクト・インスタンスやコールバック関数、どういったものでもインジェクションできます。基本構造が同じなのに見た目を変えるためだけに、上位の部分に手を加えるというのはとてもアホらしいです。CSSフレームワークの提供するパーツ単位でComponentを作って(使って)ダイレクトに記述していこうとすると、割とそういう罠に陥ります。
Contentの先にはNewsとかBlogとか色々なComponentがさらに続くでしょう。そして末端部分ではHTMLタグやclassNameを付与する事になるでしょうか。生のHTMLに近い部分は、一番末端に押し込めばいいのです。個人的には<div style={{....}} >.....</div>
のようなスタイルシートを直接いじるような野蛮なコードは好ましくなく思っているので、何らかのCSSフレームワークとclassNameの指定をする、あるいはそこらへんを一括管理できる何かを別途作成するのも良いかもしれません。
Componentの設計は抽象と具象のバランスをうまくとるセンスが必要になります。必要な情報を渡して、必要なレンダリングを最終的にこなせて、それでいてうまく疎結合にするセンスです。先ほども書きましたが、CSSフレームワークのたぐいは大抵は具象に寄りすぎているのでそこに引きずられすぎずにうまく抽象と具象を切り分ける必要があります。jQueryでいいやん、Reactとか他のフレームワークもよくわからないって言ってた人達は、そういう抽象化されたものを扱うのに不慣れなのかな?と思います。デザイナーの人であればある程度仕方ないでしょう。でも、プログラマでそういう事を言う人であれば、ちょっとスキルセットの見直しを考えるべきかも知れません。
僕はReact(や他ビュー系のフレームワーク・ライブラリ)の功績は、JSがブラウザべったりDOMべったりだったところに抽象化をもたらしたことだと思っています。うまく抽象化を進めるとユニットテストがやりやすくなるという大きな利点も見過ごせません。
これまでの時代はDOMを直接いじったりjQueryを使ったりといった野蛮なコーディングが行われていました。ですが、現代においてJavaScriptはサーバーサイドも含めて利用範囲が拡大し、高度化し続けています。ペライチならともかく、ちゃんとしたサイトを作るのであれば、野蛮さを捨てて、抽象化という文明を利用しましょう。
余談
Reactをいじるなら、Fluxは一度自作してみるといいと思います。一種のはしかみたいなヤツかもしれませんが、何をしてるのかが一目瞭然ですし、既存のFluxとかを使う時のヒントになるかもしれません。
他にJSにもたらされるべき抽象化
TypeScript or Flowで、Interface型やそれっぽいものを導入すると良いかと思います。duck typingで動的にというのでもいいのですが、TypeScriptはだいぶんカジュアルに導入しやすくなってると思います。どうせトランスパイルするのですからnpmもいろいろ導入すればいいんじゃないでしょうか?もっとも、ゴミみたいなnpmも多いですが、ES2017/TypeScriptの言語仕様でカバーできるnpmは、今の時代完全にスルーしてもいいです。
個人的なオススメはTypeScriptです。ただしFlowと同じ使い方にとどめておいて、TypeScriptの独自進化には付き合う必要はまったくありません。いつの日か、将来的にECMAScriptに型が導入された時は、おそらくTypeScriptとFlowの共通項でほとんどが構成されるはずです。
標準仕様も重要です。全ての現役ウェブブラウザはウェブの標準をもうないがしろにはしていません。どのブラウザもWebkitかBlinkかそれの完全互換(Edgeのやつ)かMozillaに集約しています。ウェブ標準を採用する障害はありません。最悪でもトランスパイラとpolyfillでほとんど吸収できます。
先日JavaScriptフレームワーク選定の議論という記事がありました。現時点ではまだフレームワーク・ライブラリが乱立している状況ですが、ある程度は共通項もありますので、なるべくならそういう共通する部分をうまく渡り歩くべきでしょう。また、今後はおそらく今よりも抽象化されたものが登場すると思いますし、そうなるべきです。
他の言語ではもっと当たり前のように抽象化が行われていました。JavaScriptがそういう点で少し遅れているとは思いますが、今後はどんどん新しいフレームワークの登場、抽象化の仕組みなどが登場していくでしょう。
まとめ
ReactはComponentを作ってなんぼ。抽象と具象のバランス感覚を磨いて、良い感じのComponentを作りましょう。そしてビューライブラリ以外のジャンルにおいても、うまく抽象化して疎結合を目指しましょう。
宣伝
- 技術書典3にて当サークル(東京ラビットハウス)受かりました。怖くないモナド本とAST本を予定しています
- わかりやすくモダンなJavaScriptの本が出ます
- VALUはじめました
- Entyはじめました
- おなかすいた