INTP型のブログ

苦味があるな?

関数型プログラミングとクラスベース設計について考える

関数型

何らかのプログラムを組むと考えた時、それはいくつかの処理からなる。その処理単位で関数化し、その関数を組み合わせて特定のプログラムを完成させる

 

クラス

何らかをモデル化し、それを触りながらプログラムを組んでいく

 

---

 

概念として理解しようとすると抽象的すぎるのでよくわからない感じがするけど、メリデメに着目するといい感じな気がする

 

関数型のメリデメ

そもそも関数型プログラミングは理念として不変性を持つことと、副作用がないことを目指すというのがある。

不変性とは、その関数を実行することで過去の値が存在しなくなるような場合。破壊的処理をできる限りやらないという感じ(例えば配列の順番を変えるにしても新しい配列にして返すみたいな)

副作用とはそれを実行することで、関数外への影響が発生するかどうかみたいな話。わかり易い例でいうと関数を実行することでグローバル変数の値が書き換えられるようなケース。これは副作用がある

 

なお、副作用に関して言うと、関数内でAPIリクエストからDBの値を書き換えるとかも副作用があるといえるので、完全に副作用のない関数型プログラミングは多分無理

 

メリットとしてテストが簡単。関数型プログラミングの理念的にステートレスでピュアな関数が多く出来上がるはずなので、その関数単体の動作が想定どおりか確認するだけで終わる(その関数を動作させるために必要な状態を用意する必要もないし、副作用がないからその関数を実行したことによる影響とかを考慮しなくて済むからテストがめっちゃ簡単ということ)

 

クラスのメリデメ

そもそも概念からムズいので下手に使わないでほしい

個人的にだけどクラスのメリットはステート管理の部分にあると思っている。

ステートとは状態のことで、例えば商品の在庫数は今100個だとする。注文されれば99,98と減る。つまり在庫の数という状態が変化する

関数型プログラミングの理念としてステートレス、つまり状態を意識したくないのでこういった状態管理をする場合矛盾が生じる(DBがステートを持つようにすれば良いような気もしてきた、副作用はあるけど)

ちょっとわかりやすい例が思いつかないので、自分がちょうど今やってるブラウザの自動テストで例えるけど、ブラウザ自動テストは現在表示されているページという状態を管理することになる。

ページの初期表示状態からクリックされたときの変化や、リンク先に移動した場合の変化など、ページの状態はどんどん変化する。

ページという状態を変更するような処理を多く作ることになるので、関数型プログラミングだと引数にpageオブジェクトを渡してそれを変更して。。で、結局テスト容易性もないし微妙だと感じた

pageオブジェクトをクラスとしてモデル化し、属性値でpageオブジェクトの状態を管理するような形にすれば、クラスでスコープが作られるので影響範囲も把握しやすい

 

そんな感じで、クラスのメリットは状態管理が適切に行えることな気がした。デメリットは設計難易度が高くなりやすいので長期的に破綻し始めることが多々あること。下手に継承使わなければ良いような気がするけど、そうすると類似処理を載せた関数を共通化できないことになるのでしんどい。関数型プログラミングはそもそも関数自体exportして、あとはファイルごとに読み込むかどうかという話になるので、その辺を意識しなくて済むのが良い

 

個人的な意見で言えば、まともなクラス設計って見たことないのでクラスはできる限り使うなと思ってる(過激派)

継承って仕組み自体が密結合の温床みたいな所あるので、下手に共通化しまくろうとすると長期運用の末、色々な仕様変更が重なって前提から変わっていって密結合しまくった処理をすべてリファクタリングするか、細かくifで条件分岐して例外的な処理を重ねまくるかの地獄の二択を迫られることになる

バカは継承を使うなというか、これは設計レベルの話なので想定される運用年数がめちゃくちゃ長いならクラス化は許すけど、継承を使うのはマジで慎重になって欲しいとは思う。長期運用した結果、どんでん返しが発生するのなんてありがちな話なのだから

 

---

 

話戻すけど、関数型プログラミングのほうが自分は保守性を高くしやすいし良いと思ってる。

 

ただ、その関数がステートレスではなく状態の変更に関心を持つのであれば、その状態をクラスの属性値として扱って、関数をメソッドとして持つというのはありだとは思う

 

ただ、何らかの状態をプログラム上で扱う必要はあるけど、それはあくまで参照値で状態変化自体はAPIを通じてDB上で行っていくというのであれば、単純に型定義して変数にぶち込んでおくのが良いと思う。変にクラス化する必要はない

 

クラス化したほうが良いケースはプログラム上で特定の値の状態を変化させていかなければならず、その状態変化に対して関心を持つ関数群がある場合だと思う。状態を変化させる必要がないなら単純な型付けした値でいいし、関数もステートレスなものとして作っていった方がいい

 

状態管理の部分をAPI通じてDB上でやっていくようなケースも同じくプログラム上でクラスとしてわざわざモデル化する必要もないような気がする。一般的な言語ではDBから取得した値はmodelsディレクトリで定義したそれ専用のクラスの属性値として持つみたいなのが一般的ではあると思うけど、下手にそれする必要あるのかとはちょっと思ってしまう。

 

まあ確かに単に変数に入れるケースだとget set両方できるので、そこがちょっと嫌かもだけど

 

この辺Rustはstructでprivate定義してあげれば外部から触れないことを保証できるのは良い点だと思う。で、そのstructに対して属性値のgetメソッドを定義してあげればgetだけできる

 

あれ、でもこれに関してはクラスでモデル化した場合も同じことできるか

 

DB上でだけ状態の変化を行いたいなら、プログラム上では取得時から値が変化されないことを保証した方がいいし、であればクラスの属性値として持ってgetメソッドを定義するような形のほうがキレイかもしれない

 

この辺も結局、仕様次第ではプログラム上で状態を変化させる必要が出てくるとかってなってクラス化した理由が薄れていくとかありそうな気もする。例えば取得後に在庫数だけ変更して更新APIにそのまま値を投げるとか。これなら変にクラス化してアクセスの制御をするよりは、単なる特定の型をつけた値として扱ったほうがわかりやすい気がする

 

やっぱ個人的にはクラスを使うシーンはプログラム上で状態変化を行う必要があり、その状態変化がプログラム全体で強い意味を持つ場合ぐらいかなぁと思った。そうでなければ関数型プログラミングのほうが長期的に見えて柔軟性が高くなりやすいような気がした

 

まあこの辺、そもそも関数型プログラミングをしやすい言語じゃないと、色々厳しいとかはあったりすると思うけれど。Laravelとかフレームワーク次第ではクラス使わない事自体無理だし。そもそも動的型付け言語だと、クラス化が実質的な型付けみたいな話になるのやもしれぬ

 

おわり