2025年2月21日

フロントエンドのデザインパターンと自律分散アーキテクチャ

上記を融合させたら、コンポーネントがより単体で輝くのではないか。 という仮説をもとに自律するコンポーネントのためのデザインパターンを考える。 その前に、どのデザインパターンが自律分散アーキテクチャに適用して実装可能かを考えてみる。

  • シングルトンパターン

    • 実装検討の価値:なさそう
    • インスタンス化は1度だけ行い、グローバルにアクセスできるようなクラスのこと。 グローバルに変数を定義してconstructorで重複チェックをすると同じクラスは作成できない対応もできる。
    • シングルトンパターンはReactだと使わない。状態管理ライブラリを使うことが良い。
  • プロキシパターン

    • 実装検討の価値:なさそう
    • オブジェクトのやり取りを自由にコントロールできる
    • オブジェクトの振る舞いを制御する手段
    • メソッドを呼び出して重い処理をするとアプリのパフォーマンスが低下する
    • Reactだとあまり馴染みがなさそう
  • プロバイダパターン

    • 実装検討の価値:ありそう
    • propsのバケツリレーなしに、データを参照することができる仕組みのこと
    • 全てのコンポーネントで利用するために囲む
    • 中央集権的な単位だとうまくいきそう
    • useContextを用いたり、状態管理を用いる
    • このパターンを使い過ぎるとパフォーマンスの問題が生じる可能性がるので注意
  • プロトタイプパターン

    • 実装検討の価値:ありそう
    • 複数の同じ型のオブジェクトを共有するために便利なパターン
    • オブジェクトが他のオブジェクトのプロパティにアクセスしたり継承したりすることが容易
    • プロトタイプチェーンにより、自身に定義されていないプロパティにアクセスできるため、プロパティやメソッドの重複が避けられて使用するメモリ容量を削減することができる
    • APIクライアントとErrorコンポーネントはプロトタイプパターンでオブジェクトとして管理するとメソッドの重複が避けられそう
  • コンテナ・プレゼンテーションパターン

    • 実装検討の価値: あり
    • 関心の分離を実現する方法の一つ
    • ビューとアプリケーションロジックを分離する
    • フェッチデータはReactのカスタムhookとしてロジックを閉じ込めておくと良い。 そうするとコンポーネント内でこのカスタムhookを呼べば、 コンテナとプレゼンテーションを分ける必要がなくなりpropsとして渡さなくても良くなる
    • プレゼンテーションコンポーネントは純粋関数になるため、テストが容易になる(データストアをモックしなくていいため)
    • React Hooksを利用するとクラスコンポーネントを作成する必要がないため、作成してしまうと不要に複雑になりかねないので注意が必要
    • この意識(ビューとロジックを分離)が自律分散性を高めるのではないか
  • オブザーバーパターン

    • 実装検討の価値: あり
    • APIを叩いてデータ操作した時に通知を出すのはこのパターンを使えそう
    • apiくらいの場所でテキストがあれば通知出すとか出来そう
    • イベントが発生すると、別のオブジェクト(Observable)にsubscribeされる。 イベントが発生するたびに、logger関数とtoastify関数が通知を受けるようになる。
    • 非同期のイベントベースのデータを扱うときに非常に便利。 データのダウンロード後にコンポーネントを通知する場合など。
    • 関心の分離と単一責任の原則を実現できる。
    • 複雑になるとSubscriberに通知する際にパフォーマンスの問題が発生する可能性がある
  • モジュールパターン

    • 実装検討の価値: あり
    • コードを再利用可能な部品へ分割
    • ファイルに名前エクスポートをする関数を作成して、importできるようにする
    • コンポーネントも本質的にはモジュールを作成している
    • エクスポートする値とエクスポートしない値を分けることでグローバルスコープを汚染するリスクを減らせる。 例えば、同じ名前を持つ値が上書きされてしまうなど。 コードの中で公開してはいけない部分をカプセル化できる。
    • ダイナミックimportすることでモジュールをインポートすることができる
    • ページの読み込み時間を短縮可能。必要なコードを必要になってから読み込んでパースしてコンパイルすればいい。
  • ミックスインパターン

  • メディエータ・ミドルウェアパターン

    • 実装検討の価値: ありかも
    • 中央のメディエータを通して、コンポーネント同士がやり取りする
    • コンポーネント同士が直接会話すると複雑になるので行わない
    • チャット機能のルーム(Mediator)とユーザー同士(Components)が例になる
  • HOC パターン

    • 実装検討の価値: なしかも
    • 同じロジック(特定のスタイル、認可を要求など)を複数のコンポーネントに使いたい場合、ロジックを使う場合は、HOCではなくフックとして扱えないかを検討した方が良さそう。HOCパターンを使用すると深くネストしたコンポーネントツリーになってしまうので注意。
  • レンダープロップパターン

    • 実装検討の価値: ありかも
    • JSXを返す関数をpropとして渡す方法。
    • renderの命名が使われる(MUIだとrenderInputなど使ってるから参考になりそう)
    • 子コンポーネントのstateを親コンポーネントに上げづらい場合は、render propパターンをつかってrender実行時に引数としてstateの値を渡すと良い。
    • React Hook FormもControllerコンポーネントのrenderメソッドはrender propパターンをつかっていそう
  • フックパターン

    • 実装検討の価値: あり
    • 従来のデザインパターンはhookで置き換えられる
    • コンポーネントのロジックを再利用可能な小さなまとまりに分割
  • フライウェイトパターン

    • 実装検討の価値: なし
    • 使用可能なRAMを全て消費してしまうような場合に役立つ
    • 現在はハードウェアがGB単位のRAMを持っているのでこのパターンの重要性は低くなっている。
  • ファクトリパターン

    • 実装検討の価値: ありかも
    • 新しいオブジェクトを作成するためにファクトリ関数を使用する
    • 小さなオブジェクトを複数作成する場合に便利。
    • Cons: 新しいオブジェクトを作成するよりも、新しいインスタンスを作成する方がメモリ効率が良い場合が多い。
  • 複合(compound)パターン

    • 実装検討の価値: ありかも
    • 自身の内部でステートを管理して、複数の子コンポーネント間で共有する
    • コンポーネントライブラリを作成する時に便利。(ということは、componentsは以下のコンポーネントはこの作り方で良さそう)
    • これを自律分散アーキテクチャで体現する一つの方法のように感じる
  • コマンドパターン

    • 実装検討の価値: なしかも
    • タスクを実行するオブジェクトとそれを呼び出すオブジェクトを切り離せる。
    • ユースケースは限られていて、不要なボイラープレートを追加してしまう可能性がある。

参照

フロントエンドのデザインパターン