FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

メンタルモデルをアップデートしてJavaへの理解を深める話

この記事はフリューAdvent Calendar 2023の1日目の記事となります。

こんにちは! ピクトリンク事業部の盛岡です。

最近めちゃくちゃ「メンタルモデル」という言葉を耳にします。 UX解説やエンジニア心得などでも頻発している印象です。
また最近は参考資料(※1)が印象的であり、プログラミングに対するメンタルモデルを見直してみる状況が生まれました。
うまくまとまるかわかりませんが、メンタルモデルをアップデートして行ってつながる話を書いていきます。
宣言しておくと、今年もポエムっぽくなります。

メンタルモデルとプログラミング

メンタルモデルとは、『人間が無自覚のうちに持っている、思い込みや価値観』のことらしいです。(※2)
人が何かの思考に到る前提として、メンタルモデルが存在します。

さてプログラミングのメンタルモデルってどんなもんだっけ?というところから見直しを始めてみます。
プログラミングのメンタルモデルは一般的に、学習初期に身に付けたもので最初は満たされ、少しづつ変化していくことになるようです。
個人的な経験としては、 以下のようなメンタルモデルを構築してきました。

  1. C言語によるプログラミングの基礎

    • システムプログラミング。構造体でデータ表現。メモリ効率も重視! (という想い)
  2. Javaに代表されるオブジェクト指向の作法や設計

    • 業務システム。メモリよりもわかりやすさ、再利用性! オブジェクトにロジックを集約させていこう!(という想い)
  3. 関数型言語プログラミング

    • 副作用は悪! 再利用→関数の合成で色々出来る!(という想い)
  4. プログラミング言語は難解なテクニックよりもわかりやすさ、よみやすさが重要!

    • 色々バランスが重要だ! 学習コストや読み手も意識しないと!(という想い)

上記のように関心事がプログラミングそのもののテクニックから、運用や共有方法に移ってきています。
最良のプログラミング手法というものではなく、ケースバイケースだとは思いますが、プログラムはシンプルにあるべきというのが現状のメンタルモデルになっています。

ソフトウェア設計手法に関する認識

また、読み手を意識したプログラムを考察するにあたって、プログラミングそのものの関心から設計への関心が移っていくのを実感しています。その比率は年々高まっている印象です。

  1. 実装がうまく出来ればソフトウェア開発はうまく行く(という思い込み)
  2. 実装をがんばるだけでなく、再利用性を考慮したり、先人が考えた偉大なフレームワークに乗っかるなどでやればうまく行く(という思い込み)
  3. 大規模運用や保守性を考慮するには、ビジネスドメインの理解や組織的な設計まで踏み込む必要がある。うまく進めるのはとても大変(という思い込み)

まだまだ理解が足りてないとは思いますが、大体ここ3年くらいは一所懸命ビジネスや組織を考えたりしてます。

さて、現在ソフトウェアの設計というとDDDが良く挙がる印象です。ただ、DDDのすべての要素をうまく使いこなせるかというと、なかなか出来るものでは(個人的には)ありませんでした。 DDDに関しては、いきなりソフトウェアの設計への組み込みというよりは、業務の分析とドメインモデルの抽出というところがキモなのかなという現在認識です。この辺りはもう少し認識のアップデートかけていきたいところですね。

一方DDDをベースとしたソフトウェア設計のメリットとして、セキュア・バイ・デザイン(※3)による堅牢性の確保というところでは、認知が出来てきた印象です。
その上で、ドメインロジックと外界とのインターフェースに注意を集中するとそれなりにうまくいくことが多いと感じております。また、どこを外界と置くのかによって設計や実装の塩梅が変わりそうな印象も持っています。


https://www.amazon.co.jp/dp/483997599X

外界とのインターフェースと型定義に関する認識

「セキュア・バイ・デザイン」でドメインプリミティブを利用して、値の範囲や条件をモデルとして定義し、保証しましょうというのが口酸っぱく解説されていました。
あまり内部詳細までドメインプリミティブ化するのは、しんどいんじゃないの説はありつつ、外界とのインターフェースを考えるときには有用と感じます。

資料(※1)では、ドメインロジックの入力を「ある値の集合」と考えたときに、ドメインロジックは写像という話を展開されてます。
オニオンアーキテクチャで外界から分離されたドメインロジックを関数としてとらえるとのは一考の価値がありそうです。そのような文献も紹介されてました(※4)。
また「ある値の集合」を表現するには、型として定義出来ればプログラミングとして扱いやすくセキュアであるというのはスッと腹落ちしました。

また型の表現においては、代数的にデータ型を表現できるとさらに良いことがあると知りました。

例)

type Account = MiniAccount | StandardAccount

継承を用いるようなAndだけでなく、どちらか一方の型を持つOrとして型が定義できると値の集合表現としてより限定的になりそうです。
(ちなみに、上記は Account は MiniAccount StandardAccount を取る型ですよ.. というざっくり解説)

TypeScriptの型の "和(Or)” が取れることは知識としては認識していたのですが、ドメインロジックでの活用を認知したことで、型が値空間の表現であるという実感や、代数的データ型が存在する意義などが自分の中でアップデートされるのを感じました。代数的データ型便利やん。

Javaでの代数的データ型に関する認識

そしてType Script以外の言語ではどうなっているのかに興味が移っていきます。(Haskellは当たり前のように出来るんだなぁという印象...)
現在我々が運営しているピクトリンクのサーバサイドは、Javaによる実装が多数を占めています。
Javaでは型の和はあるのでしょうか?

こちらは "Sealed Classes" によりこれは実現可能なようです。

先程の例の Account において、Javaでは以下のような表現が可能になります。

sealed interface Account permits MiniAccount, StandardAccount {
    long getId();
}

record MiniAccount(long id) implements Account {
     public long getId() {
         return id;
     }
}

record StandardAccount(long id, Date exipreDate) implements Account {
    public long getId() {
        return 0;
    }

    public long secondsUntilExpiry() {
         // TODO 期限切れまでの秒数を返す
    }
}

ドメインロジックの引数としてSeald Classesを使うとセキュアに入力範囲や条件を記載出来る可能性が高まりますね。
サンプルは適当ですが、各Accountの状態/種別に適合した処理をヌケモレなく記載出来ます。

// ドメインロジックのExample。expireしそうなStandard Accountに別名を与える
public String domainLogicExample(Account account) {
    return switch (account) {
        case MiniAccount(var id) ->
            "mini";
        case StandardAccount standardAccount -> {
             if(standardAccount.secondsUntilExpiry() > ONE_DAY) {
                 yield "standard";
             } else {
                 yield "expiring_standard";
             }
        } 
    };
}

Javaへのメンタルモデルをアップデート

Javaは5→8→11→17→21と着実に実用的なアップデートが加えられていってます。
元々は Write once, run anyware という謳い文句やオブジェクト指向を全面に押し出していましたが、現状ではデータ構造やデータ定義を強化していってる印象です。また、関数型プログラミングの要素も取り入れつつパフォーマンスにも妥協しない、エンタープライズで使いやすい言語という認識と(個人的には)なってます。

JavaからDOPの話題に関する認識

最近のJavaの文献や動向を調べているうちにDOP(データ指向プログラミング)という概念も良く見かけられました。
書籍(※5)を読んだ程度の理解はあったのですが、なかなか業務利用やJavaへの適用は難しいかなと考えていました。 参考資料(※6)を確認すると、Javaとセットで語られるDOPは、書籍での話とはまた別の世界線にあるDOPのようです。メンタルモデルがアップデートしまくりですね。

ただ、Javaの現状フォーカスしているデータや処理の扱いを考慮すると、書籍DOPの4つの原則とまったくかぶらないわけではなく

  1. コードとデータを分離する
  2. 汎用データ構造でデータエンティティを表現する
  3. データはイミュータブルである
  4. データ表現からデータスキーマを切り離す

における、3は言うまでもなく、2や4の一旦はJavaではrecordやクラスで表現されるのかなという印象です。
今ふわっと思っているのは、

  • DDDによる分析、境界やコンテキストの把握
  • 境界におけるセキュア・バイ・デザイン的な型定義の活用
  • DOPによるシンプルなデータ定義と取りまわし

などが個人的にはシンプルかつ堅牢への糸口では?という感覚です。(シンプルに考えるために良いところをつまみ食いみたいなイメージもある。。)

まとめ

まとめると、様々な分野でのメンタルモデルが解きほぐされてアップデートされた実感があります。

長年ソフトウェアの業界に努めていると、ある種の固定観念が出来上がります。
最近ではアンラーン(Unlearn)を行い、新しい認識をスムーズに獲得するような動きが推奨されてきてます。
今回の記事では、ちょっとしたきっかけから、Javaの現状や今どきの設計手法の一旦がチョトワカル気持ちになりました。立ち止まってアンラーンする時間も今後は重視していきたい気持ちです。

それでは、2日目以降もお楽しみください!


参考文献