JavaScriptと防御的プログラミング
JavaScriptで堅牢なアプリを作る
JavaScriptによる動的なUIを持つWebサイトがあふれる昨今、皆様はいかがお過ごしでしょうか。
私も例にもれずSPAなどを作ってコツコツリリースしています。
数打てばどれか当たるだろうという淡い期待と、蓄積される関連ノウハウと共に。
まぁ、このブログはSPAじゃないんですけどね。
最近、3DゲームをJavaScriptで作りました。
技術的には Django,Django REST Framework, Vue, Vuex, THREE.js を使って10日ぐらいで作りました。
で、このゲームは迷路をユーザーが自由に作れるというアプリなんですが、迷路エディターというのがあります。
この迷路エディターで作った迷路のデータはCSVでサーバーサイドに保存されます。
なんでJSONじゃなくてCSV? なんて思われた方もいるかもしれませんが、データの圧縮度などを考慮すると今回の場合はCSVがベターなデータフォーマットだったのです。
で、CSVのデータをクライアントサイドでオブジェクトに相互変換する処理を内部で持っているのですが、この相互変換処理がなかなか大変です。
というのも、データのフォーマットに不正があった場合、それを検出しないと、サーバーサイドに不正なデータを保存してしまう可能性があるからです。
なのでデータの整合性はクライアントサイド、つまりJavaScriptで念入りに検証する必要があるのですね。
まぁサーバーサイドでももちろん検証はしたほうがよりセキュアなアプリになるのですが……。冗長性というやつですね。
それでJavaScriptでデータを検証する場合、型を考慮する必要が出てきます。
JavaScriptで型を判定するには typeof
や Array.isArray
を使います。
:::javascript
function func (arg) {
if (typeof arg !== 'string') {
throw new Error('error')
}
}
セッターなどを使ってオブジェクトにデータをセットする時に、この型判定をしておかないと、不正なデータ型がメンバ変数にセットされる可能性があります。
そしてオブジェクトに不正なデータがセットされたまま、オブジェクトをシリアライズすると不正なデータが送信データに乗ってしまうということですね。
なのでセッターなどに型判定を入れて、不正な型やデータは例外を投げるようにしておきます。
:::javascript
class MyObject {
constructor () {
this.data = null
}
setData (data) {
if (typeof data !== 'string') {
throw new Error('invalid type')
}
if (data.length === 0 || data.length > 32) {
throw new Error('invalid data length')
}
this.data = data
}
}
このようなプログラミングを俗に「防御的プログラミング」と言います。
設定されるデータを検証するようにして、防御的にデータを扱うということですね。
このように防御的プログラミングをJavaScriptに導入すると、3Dダンジョン迷路のようなわりと厳格さが要求されるアプリでも、ある程度の品質を保証することが出来るようになります。
これに加えて、最終的なデータを検証するバリデータを定義しておけば鬼に金棒です。プログラムが厳格に振る舞おうと努力するので、エラーなどが開発時にたくさん出るようになります。
そしてそのエラーを潰していけば、最終的にある程度堅牢なアプリが出来上がるという話ですね。
さらにその検証に冗長性を持たせれば完璧かもしれません。3Dダンジョン迷路ではそこまではやっていませんが。
それで、こういった防御的プログラミングをJavaScriptで書いていると、やはり最終的に欲しくなるのは「型」なんですね。
なので、それならTypeScriptを使えば? と言う話になります。
私は動的型付けが好きな性格なのでJavaScriptとかPythonが好きなんですが、まぁ、C/C++をやっていた反動でしょうね。
しかしまた型が必要になるとは思いませんでした。
堅牢なアプリには型か、型判定が必要ということですね。
3Dダンジョン迷路、よかったプレイしてみてください。