Webベースのグラフィックエディタのアーキテクチャと7つのデザインパターン(1)
*この記事は、FEConf 2023で発表された『The Architecture of Web-Based Graphic Editors and 7 Design Patterns』のセッションを要約したものです。発表内容は2つのパートに分けて公開します。Part 1ではWebベースのグラフィックエディタの基本的なアーキテクチャと、その中に組み込まれているデザインパターンについて解説します。Part 2では、実際にグラフィックエディタを実装し、その問題を解決していく過程で、デザインパターンをより深く掘り下げていきます。この記事の画像はすべて同名の発表スライドから引用しており、個別の出典表記は省略しています。発表スライドはFEConf 2023のウェブサイトからダウンロードできます。
「Webベースのグラフィックエディタのアーキテクチャと7つのデザインパターン」 / Heungwoon Shim、Naver フロントエンドエンジニア、FEConf 2023で発表
こんにちは、Naverでプラットフォーム開発を担当しているフロントエンドエンジニアのシム・フンウンです。この記事では、Webベースのグラフィックエディタとデザインパターンについて探求していきます。
私は前職と現職で、5つのグラフィックエディタ開発プロジェクトに参加しました。UIプロトタイピングツール、グラフィックエディタフレームワーク、ビットマップ画像エディタ、機械学習の学習データを生成するためのアノテーションツールなどです。これら5つのプロジェクトはすべて、EclipseのサブプロジェクトであるGEFという一つのルーツから派生しています。
Eclipse GEF
GEFは2000年代初頭に始まった歴史の長いプロジェクトです。Eclipseに統合されており、様々なEclipseモデルをGUIで編集するために使用されます。当時、私たちのチームはこのGEFのアーキテクチャをプロジェクトに適用し、その過程でグラフィックエディタには多種多様なデザインパターンが組み込まれていることを発見しました。進化し続けるWebエコシステムに対応するために改良を重ねてきましたが、その中に存在するデザインパターンは今もなお有効です。
この知識を共有することで、他の方々が実践的な観点からデザインパターンを理解する助けになればと思いました。この記事がWebベースのグラフィックエディタプロジェクトのアイデアを提供し、デザインパターンを業務に適用したいと考えている方々にとって良い出発点となることを願っています。
この記事で扱う内容は以下の通りです。
- Webベースのグラフィックエディタの基本的なアーキテクチャ
- 関連するデザインパターンとその解決策
デザインパターンの再考
「パターン」という言葉は、テンプレート、デザイン、サンプル、モデルなどを意味します。フランス語のpatronに由来し、繰り返し起こる事象や物体の形式を指します。簡単な例としては、蜂の巣や建築物に見られる幾何学的な繰り返しが挙げられます。
アーキテクチャとパターン
エンジニアリングの観点から見ると、パターンの概念は建築家クリストファー・アレグザンダーの著書『パタン・ランゲージ:環境設計の手引』で初めて紹介されました。この本は、私たちの生活環境を構成する要素を正しく配置するための253のパターンを紹介しています。下の画像にあるようなグリッド、トポロジー、ネットワークといった概念は、どこか見覚えがありませんか?
著書「パタン・ランゲージ」
私たちがコードの中にデザインパターンを見出すように、著者は空間や建物の中に繰り返し現れる要素を観察し、他の要素との関係について考察しました。私たちがよく使う「ポータル」という言葉が、実は建築に由来することをご存知でしたか?『建築を変えた100のアイデア』という本を見ると、見慣れた言葉がたくさん出てきます。
著書「建築を変えた100のアイデア」
モジュール、レイヤー、コンポジション、スタイル、プラットフォーム、コンテキストなど、それらの抽象的な意味合いも私たちが使っているものと非常に似ています。このように、建築とプログラミングには多くの共通点があります。この意味で、建築家が物理的な空間を設計する人であるならば、プログラマー、特にフロントエンド開発者は、精神的な空間を設計する建築家と見なせるかもしれません。
プログラミングとパターン
下の本は、かの有名な『デザインパターン』です。1994年に、EclipseやVisual Studio Codeの設計者であるエリック・ガンマが、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースと共に執筆しました。この本によって、プログラミング設計におけるパターンの概念が広く知られるようになりました。
著書「デザインパターン」
この本では、ソフトウェアコンポーネントを適切に設計するための23のパターンが紹介されており、その人気によってさらに多くのパターンが発見・作成されるようになりました。
デザインパターン
では、デザインパターンとは一体何なのでしょうか?Wikipediaの定義によると、「設計ノウハウの蓄積を、名前を付けて整理し、再利用可能な形式にしたもの」とされています。もっと簡単に言えば、特定の文脈で繰り返し発生する問題に対する解決策です。
デザインパターンは発明されるものではなく、経験を通じて発見されるものです。DRY(Don't Repeat Yourself)がコードの再利用に関するものであるのに対し、DP(Design Pattern)は経験の再利用に関するものです。
ソフトウェアのデザインパターンは、大まかに次のように分類できます。
- アーキテクチャパターン: MVC / Pub-Sub / DAO / DTO / Broker
- GoF(Gang of Four)パターン: 生成 / 構造 / 振る舞い
- 並行性パターン: イベント駆動 / スケジューラ / リアクター
これら以外にも多くのデザインパターンが存在します。この記事では、以下でハイライトされている7つのパターンを紹介します。
この記事で取り上げるデザインパターン
デザインパターンの利点
-
良い設計は実装をシンプルにする。
設計なしで実装を始めると、最初は速く感じるかもしれませんが、時間とともに非効率性と複雑性が急速に増加します。 -
効果的なコミュニケーションを可能にする。
システムを説明する際にパターンを用いることで、シンプルかつ正確なコミュニケーションが可能になります。誤解を減らし、全員がより効率的に作業するのに役立ちます。 -
オブジェクト指向の原則を遵守するのに役立つ。
ソフトウェア品質の基礎であるSOLID原則の目標は、再利用性、拡張性、保守性を高めることです。デザインパターンは基本的にSOLID原則に基づいているため、習得することで柔軟で拡張性の高いコードを書く助けになります。 -
デザインファーストの考え方を育む。
デザインパターンはあなたのメンタルモデルの一部になります。問題の多い大規模なコードベースをリファクタリングする際、デザインパターンを知っていると、全体の流れをより効果的な構造に再設計するのに役立ちます。 -
高い視点を持つことができる。
高い視点を持つことで、全体像を把握できます。デザインパターンは、ライブラリやフレームワークよりも高レベルの概念です。そのため、デザインパターンを理解していれば、フレームワークやコードをより容易に理解し、その背後にある意図をより明確に把握できます。
デザインパターンはどのように問題を解決するのか
デザインパターンが問題を解決する方法は、3つの観点から考えることができます。
-
変更を容易にする方法を提供する。
デザインパターンの原則は、ソフトウェアの変更と深く関連しています。そのため、その中核的な機能は、システムの特定の部分を容易に変更できるようにすることです。 -
抽象化と委譲。
変更を容易にするために、変動する部分の処理を専門のコンポーネントに委譲します。システムの変更される部分を分離し、抽象化し、インターフェースを介して専門のモジュールに作業を委譲します。 -
継承よりコンポジション。
そして、継承ではなくコンポジションを用いて問題を解決します。その理由は、継承はコンパイル時に決定されるのに対し、コンポジションは実行時に決定されるため、より柔軟性が高いからです。このため、下の画像に示すように、GoFパターンのほとんどはコンポジションに基づいています。
デザインパターンが問題を解決する方法
デザインパターンはいつ使うと良いのか?
システムの特定の部分が継続的に変更されると予測される場合に使用するのが良いでしょう。これが、デザインパターンがフレームワークで頻繁に使用される理由です。フレームワークは基盤を提供し、変更可能な部分を交換しながら開発が進められます。また、コード内のif
文を常に修正していることに気づいた場合も、デザインパターンの使用を検討する良いタイミングです。
デザインパターンはどのように使うべきか?
シンプルにKISSの原則に従うだけです。この原則はアメリカ海軍で開発されたもので、「Keep It Small and Simple(小さく、シンプルに保て)」という意味です。つまり、デザインパターンが問題を単純化する場合に使うのが最善です。ただし、唯一の正解はありません。デザインパターンは「望ましい経験」であるため、状況に応じて柔軟に適用すべきです。
描画のデザインパターン
では、描画という行為に関わるデザインパターンを探ってみましょう。まず、一枚の紙に絵を描くという編集作業について考えてみます。紙に絵を描くプロセスは、以下のステップで進みます。
- 鉛筆を動かす。
- 黒鉛が紙に移る。
- 紙の状態が変化する。
では、プログラムが絵を描くプロセスはどうでしょうか?
- 入力デバイスを動かす。
- イベントが配信される。
- 画面の状態が変化する。
要するに、編集タスクとはイベントを状態変化に変換するプロセスです。ここで、ユーザーがイベントを入力すると、紙やモニターの画面が変化することを期待します。これがユーザーのメンタルモデルです。
メンタルモデル
したがって、このプロセスをもう一度要約すると、編集タスクはユーザーの意図、つまりメンタルモデルを、イベントを通じてコンピューターモデルに変換するプロセスとして説明できます。
編集タスクの特性
MVCパターン
先ほど説明したプロセスのために設計されたアーキテクチャがあります。1976年にXerox PARCでノルウェーのコンピューター科学者トリグヴェ・リーンスカウグによって考案されたMVCパターンです。このモデルでは、Viewがメンタルモデル、Controllerが編集デバイス、Modelがコンピューターモデルを担当します。
MVCと編集タスク
パロアルト研究所は、私たちが今日享受している多くのITデバイスのプロトタイプが開発された場所です。当時、彼はiPadの遠い祖先とも言えるDynabookを開発するチームに所属していました。このチームを率いていたのは、オブジェクト指向プログラミングとGUIのパイオニアであり、Smalltalk(オブジェクト指向言語の先駆け)の開発も主導したアラン・ケイでした。
チームは、ユーザーがより使いやすくなるようにGUIの改善に注力していました。この過程で、トリグヴェ・リーンスカウグはある気づきを得ました。それは、ユーザーのメンタルモデルと、コンピューターの情報処理方法が異なるということです。
そこで、トリグヴェ・リーンスカウグは、メンタルモデルをコンピューターモデルに変換するプロセスのための構造としてMVCを考案しました。下の画像は、リーンスカウグがMVCについて執筆した論文の一部です。論文には次のような一節があります。「この話は、人間と機械の間のギャップを埋めるという問題に焦点を当てている。」
人間と機械の間のギャップ - MVC論文より
MVCの本質とそれが解決すること
彼は次のように述べています。「MVCの基本的な目的は、ユーザーのメンタルモデルとコンピューター内に存在するデジタルモデルとの間のギャップを埋めることである。」「理想的なMVCソリューションは、ユーザーがドメイン情報を直接見て操作しているかのような錯覚をサポートする。」「この構造は、ユーザーが同じモデルを同時に異なる視点から見る必要がある状況でも役立つ。」
MVCが解決すること
MVCパターンにおける関心の分離
このパターンの核心は、役割に基づいてモジュールを分離すること、つまり関心の分離です。これを行うことで、MVCパターンはメンタルモデルをコンピューターモデルに効果的に変換できます。これが、MVCとその派生パターンが40年経った今でも様々なアーキテクチャの基礎として使用されている理由です。
MVCの関心の分離
現代のMVCは、状況に合わせて少し変更された形で構成されることが多いですが、標準的な構造は下の図のようになります。ControllerがViewを介してユーザーイベントを受け取ると、Modelを修正するためのコマンドを作成して送信します。Modelが修正されると、その変更に基づいてViewが更新されます。このMVCパターンは、実際には他のいくつかのパターンを組み合わせた複合パターンです。
現代のMVCパターンの構造
Modelは、ObserverパターンのSubjectの典型的な例です。ViewとControllerはModelの状態変化を監視しますが、Modelはそれらの存在を意識すべきではありません。また、ViewやControllerがライフサイクル中に破棄される際には、その購読を解除する必要があります。
ModelにおけるObserverパターン
Viewを構成するGUIコンポーネントは、ウィンドウ、パネル、ラベル、ボタンのように、しばしば入れ子構造で構成されます。Compositeパターンは、このような入れ子構造を単一のオブジェクトとして扱い、入れ子を抽象化するパターンです。
ViewにおけるCompositeパターン
Strategyパターンは、Controllerの典型的なパターンです。ViewはイベントをControllerに渡すだけで、Modelの変更には関与しません。Modelをどのように変更するかは、Controllerのロジックによって決定されます。そして、Viewに接続されたこのControllerは、実行時に変更することができます。
ControllerにおけるStrategyパターン
これらのパターンについては、後のセクションで詳しく見ていきます。
グラフィックエディタのアーキテクチャ
では、グラフィックエディタの基本的な構造について学びましょう。下の図は、グラフィックエディタの外部構造を示しています。周囲にはワークベンチがあり、メニューとツールバー、左側にはツールがあります。中央にはキャンバスがあり、右側には上から順にプロパティ、パレット、ヒストリー、レイヤーパネルがあります。最後に、一番下にはステータスバーが見えます。
グラフィックエディタの外部構造
このセクションでは、図でオレンジ色にハイライトされているツール、キャンバス、ヒストリーの領域を実装することで、デザインパターンを探求します。
内部コンポーネント
内部構造はアプリケーションによって異なるため、一般化するのは難しいですが、他のアプリケーションも同様のコンポーネントを持っている可能性が高いと考えています。グラフィックエディタは、内部的に以下の要素で構成されています。
グラフィックエディタの内部コンポーネント
- Model: エディタが操作できる独自のドメインモデル。
- Graphic Viewer: グラフィックイベントに基づいて編集が行われる空間。
- Event Dispatcher: Webブラウザからの低レベルイベントを、エディタ用の高レベルイベントに変換します。
- Command Stack: UndoとRedoをサポートすることで、履歴を管理します。
- Root Part: 編集の基本単位は「Part」です。Partは編集アクションの実行を担当します。
内部コンポーネントのRootPart
- Tool: 外部構造のツールを実装します。
- Action Registry: キーボードショートカットを処理します。
- Request: イベントを抽象化したものです。
- EditPolicy: 実際の編集を実行するマイクロコントローラーです。
次回の記事では、これらの内部コンポーネントについてより詳しく紹介し、仮想のグラフィックエディタを実装することでデザインパターンをどのように適用するかを探求していきます。
Discussion