2021/04/072021/03/15

クラスを使わないアプリを実際に作ってみた気付き

ども、北米でお勉強してるNashです。

この記事は「クラスを使わないでアプリケーションを作ってみたことによる気付き」をまとめた記事です。

クラスを使わないアプリを作った

先に結論だけども、クラスなしのプロジェクトはあまりおすすめできないので素直にクラスを使うか、immerと併用するのが良いかと思う。

ということで、1つずつ見ていく。

何を作ったの

作ったものとして『Plangoab』というWebカレンダーサービスを作った。想定する利用者として、海外へ渡航して現地就職を目指す人またはそれを支援する留学エージェントになる。

Plangoab | Plan to go abroad

なんで作ったの

理由の1つとしてはポートフォリオ用のプロジェクトが必要だから。カナダで就活をする予定なんだけど、そのときに技術証明をする必要があるので作っておいた。

実際にこのアプリをResumeやLinkedinに載せてるので、こっちのインタビューとかでも軽く聞かれる。

他の理由として、海外渡航〜就職のスケジュールを実際に考えるのが面倒だった、という自分の経験からこれを解決したいという思いがあったので。詳細はREADMEに、もちっと詳細に書いてるのでそっち参照で。

GitHub - snamiki1212/plangoab: A web calendar for a person going abroad. Generating a suitable schedule and customizable. Using React, Redux, and FullCalendar.

技術スタックは?

これもREADMEに書いてあるが、このブログを書いてる時点では少なくとも下記が主要なものになる。

  • TypeScript / React / Redux

GitHub - snamiki1212/plangoab: A web calendar for a person going abroad. Generating a suitable schedule and customizable. Using React, Redux, and FullCalendar.

なんでクラスなしで作ったの

というわけで、この記事の主題に戻ってきた。

根本的なところは「クラスなしでアプリを作ったらどうなるんだろう?」を試してみたかったからという好奇心。あと、「きっとこうだろう」という憶測じゃなくて実体験から得られるナレッジもほしかったから。

2021年時点のOOPへの評価

最近のプログラミングパラダイムを見ていても「OOPって本当に必要?」なところに言及してる日本語ツイートをチラホラ見る。

どちらかというと「継承って必要ないよね」の文脈のほうが正確だとは思うし、実際Rust・Goみたいな次世代のプログラミング言語ではダックタイピングプログラミングを採用してる。継承はツラミがありすぎるよね、ってのがRustのチュートリアルドキュメントにも書かれてるくらいだしね。

破壊的変更がありえるというクラスが好きじゃない

これは特に個人的な感性。

OOPの思想として、メソッドを呼び出すと破壊的変更が発生する「かもしれない」ってのが好きではない。明示的ではなく、結局は内部実装を見て理解しないとメソッドを怖くて使えないってのが気に入らないポイント。


というわけで、いくつかの理由はあるけど、とにかく「クラスなし」でアプリを作ってみたくなった。

どんな感じでクラスなしのアプリを作ったの

「クラスを使わない」ってのはわかったけど、じゃあ「モデルに相当するデータをどうやって扱うの?」に対する答えとして下記になる。

  • データ:Plain JavaScript Object
  • データ操作:関数

特に操作に関しては関数化したものをModelの1箇所に集めておくことにした。データ操作したい場合に直接そのデータを取り扱うことは禁止で必ずModelから関数をImportして呼び出すにした。

クラスなしのアプリを作った結果

シリアライズをする必要がなくなった

今回作ったアプリケーションは下記の2つの特徴がある。

  • Reduxを使っている
  • バックエンドがない

そのため、まずReduxだがStoreへのデータ格納はPlain JavaScript Objectをそのままブチ込めばよいだけなので楽だった。インスタンスだとそのまま格納できないので。

あともしバックエンドがある場合はバックエンド⇔フロントエンドの間でもデータ変換が必要になる。つまり、Plain JavaScript Pbject⇔インスタンスの変換を都度しないといけないと思うが、そこもなくなる。

というわけで、ここのメリットを明示的に書くとしたら、シリアライズコスト(速度・メモリ・コード量・バグ発生可能性)がゼロになった。

データ+操作のカプセル化消滅は気合でカバー

インスタンスではなくてPlain JavaScript Objectになったことでデータをどこでも気軽に変えられちゃうようになった。

これはとんでもないヤバみである。クラスがなくなりカプセル化ができなくなったわけだ。誘惑に負けて気軽にデータへの操作をそこらへんで書き出すと速攻でやばいコードになる。

このプロジェクトでは「データに対して操作をする場合はmodelファイルに集約した関数をimportして使うこと」を自分でルール化していた。なので、データと操作が散らばることはなくて、やばいことにはならなかった。

ただ、この解決方法って仕組み化ではなくて「気をつけていた」という解決方法になる。なので、気付かなかった、面倒だから、みたいな理由とかでルール破りが発生するとコードが一気にスパゲッティ化する。

今回は一人でやってるプロジェクトだったので問題にはならなかったけど、チームでやると結構なヤバみだと思う。というか、すでに一人のブロジェクトですら1箇所に責務のある関数を集める+そこから毎回呼び出すってのが面倒だなーとなっていたので、複数人で開発すると速攻で破綻すると思う。

ちなみに、ふとここの章を書いてる途中に「Plain JavaScript Objectに関数をもたせて thisでデータ操作すれば良いじゃん」みたいなことを頭をよぎったけど、それだと本末転倒すぎてもうよくわからなくなった。

クラスを使わないということは、クラスを使わないで済む(進次郎構文)

最近のTypeScript+JavaScriptのクラス構文のもろもろを知らなくても書ける。メリットと読んでよいのかよくわからんけど。

あと、クラスがないので、必然的に継承も発生しない。

所感:無秩序クラスなしプロジェクト

今回は「1箇所にデータ操作をする関数を集める」という方針だったけど、それすらやめてどこでもデータ操作を可能にすると無秩序になるなー、とひしひしと感じた。

仮に、めちゃくちゃ小さいアプリでざっと書く場合はこの無秩序なクラスなしは選択肢としてありえて、つまり、「どこでも手軽に書き換え可能」のメリットのほうが大きいケースならありえる。

とはいえ、その結果「プロジェクト全体で責務がバラバラでカオスになる」に至る損益分岐点が、かなり早い段階で来ちゃうと思うし、そこからスケールも出来ないので無秩序クラスなしで書くケースはほとんどないとも思う。


というわけで、クラスなしのアプリを作った結果をつらつら書いてみた。

「クラス使わない」に対する代替方法

今回、クラスなしのプロジェクトを作ってみたけど、それに変わるちょうどよい塩梅の代替方法ってないの?に回答として「immer + Classの併用」が個人的には一番良い塩梅だと思ってる。

そもそもなんでクラス使いたくないんだっけ

クラスを使いたくない理由として、すでに上述してるけど自分の場合は「破壊的メソッドが好きでない」が最大の理由になる。

OOPの思想として、メソッドを呼び出すと破壊的変更が発生するかもしれない、ってのが好きではない。明示的ではないので、結局は内部実装を見て理解しないと怖くて使えない。

代替方法「Classes immer」

これに対する良い塩梅の回答として「immerをClassに対して使え」になる。

https://immerjs.github.io/immer/docs/complex-objects

詳細は上述のリンクに書いてあるけれども、インスタンスのプロパティがReadonly化されてImmutableになり破壊的変更ができなくなる。破壊的変更をしたい場合はインスタンスの作り直しになり、ロジックとしてimmerを使って書かないといけないのでちょっと気持ち悪いけど、その気持ち悪さもクラス内部にカプセル化されるので、外側に漏れることがない。

代替方法「Immutable.js」(非推奨)

https://immutable-js.github.io/immutable-js/

Immutable.jsを使うという選択肢もあるけど個人的にはおすすめできない、ということだけを記述しておきたい

  • Immutable.jsのAPIを覚えないといけない
  • Immutable.jsのAPIとPlain JavaScriptのAPIが混同する
  • 間違えてPlan JavaScriptのAPIを使ってもエラーが発生しないケースがある

過去にジョインしてたプロジェクトで使ってたけど、何回こいつのせいでバグを生み出したかわからないってくらいハマったし、毎回APIの呼び出し方を調べないといけなくて生産性も激下がりだったので、immerだけ勧めておきます。

所感

OOPのメリットをぶっ潰すようなムーブとして、あえてクラスを使わないでアプリケーションを作ることでいい感じのヤバさを肌で感じることができる経験だった。

個人的にこれはプロダクションでは採用してはいけないアーキテクチャだと感じる。

なので、次になにかを個人で作ったりこのプロジェクトを大きくリファクタしないといけないなら上述したClasses Immerの代替方法で作ると思う。

(追記)小さい個人プロジェクトを作る機会があったのでClassesImmerを試してみたけどやはり肌感としてはかなり使い勝手が良かった、と記しておく。

https://github.com/snamiki1212/sorting-algorithm-animation

おわりに

クラスを憎まず仲良くなりましょう。

Nash
Nash
プログラミングが好きな人。SE→ITベンチャー→フリーランス。日本を出て、海外で働いて、最終ゴールは月で生活すること。