GoF以外のプログラミング・デザインパターン #03 MVVM

2025/11/24

プログラミング 学習

t f B! P L
eyecatch MVVM(Model–View–ViewModel)パターンは、MVPの欠点(ViewとPresenterの結合の強さ)を解消して、双方向データバインディングによって、UIとデータを自動同期させることができるパターンです。 2005年頃に、MicrosoftがWPF(Windows Presentation Foundation)で提唱されました。 現在は React / Vue / Angular / Svelte などの基礎概念にもなっていますね。

構造

View(UI)

HTML, DOM ↓↑ 双方向データバインディング

ViewModel(UIロジック)

Model(データ)

各役割の説明

Model(モデル)

・データやビジネスロジック。 ・APIやDBとの通信を担当。

View(ビュー)

・UI要素(HTML, DOM)。 ・自身ではロジックを持たず、ViewModelとデータを自動同期。

ViewModel(ビューモデル)

・Viewに表示するための中間ロジック。 ・Viewの状態(文字、ボタン状態など)を保持。 ・Modelのデータを変換・管理。 ・双方向データバインディングを担う。

サンプルコード

<div id="app"> <h1>{{ count }}</h1> <button data-action="increment">+</button> <button data-action="decrement">−</button> </div> <script> class Reactive { constructor(data) { this.listeners = []; this.data = new Proxy(data, { set: (obj, key, value) => { obj[key] = value; this.notify(); return true; } }); } bind(callback) { this.listeners.push(callback); callback(this.data); } notify() { this.listeners.forEach(cb => cb(this.data)); } } // Model class CounterModel { constructor() { this.count = 0; } increment() { this.count++; } decrement() { this.count--; } } // ViewModel class CounterViewModel { constructor(model) { this.state = new Reactive({ count: model.count }); this.model = model; } increment() { this.model.increment(); this.state.data.count = this.model.count; } decrement() { this.model.decrement(); this.state.data.count = this.model.count; } } // View class CounterView { constructor(vm) { this.vm = vm; this.el = document.getElementById("app"); this.bindEvents(); this.vm.state.bind(data => this.render(data)); } bindEvents() { this.el.addEventListener("click", e => { const action = e.target.dataset.action; if (action && this.vm[action]) this.vm[action](); }); } render(data) { this.el.querySelector("h1").textContent = data.count; } } // 起動 const model = new CounterModel(); const vm = new CounterViewModel(model); new CounterView(vm); </script>

ポイント解説

・View と ViewModel が データバインディング によって自動同期。 ・イベントハンドラを ViewModel に集約。 ・DOM操作(document.getElementByIdなど)をほぼ書かなくてよい。

特徴まとめ

要素 役割 特徴
Model データ・ロジック ViewModelにのみ依存
View UI表示 データの変更を自動反映
ViewModel 中間ロジック 双方向データバインディング担当

メリット

・Viewとロジックの分離が徹底。 ・テストが容易(Viewを操作せずに状態確認できる)。 ・コードの再利用性が高い。 ・双方向バインディングでUI更新が簡潔。

デメリット

・双方向データバインディングの仕組みが複雑になりやすい。 ・大規模アプリでは依存関係が見えづらくなる。 ・デバッグが難しくなることもある。

使われている場面

技術 MVVM的構造
Vue.js `data()` ↔ テンプレートの自動バインディング
React(Hooks) 状態とViewの一方向同期(MVVMの変形)
Angular `ngModel` などで双方向データバインディング
Knockout.js MVVMを直接実装した初期ライブラリ
SwiftUI / Jetpack Compose 状態駆動UI(MVVM思想)

あとがき

JS系のためのデザインパターンと言っても過言ではない、MVVMパターンです。 DOM操作を円滑に行うために、MVPを改良したパターンですね。 構造が少し複雑になるのが気になりますが、自分なりに、使いやすい構造ルールを作ると、便利に活用できそうです。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ