グラフィック系のModel-Viewを考える

手元のコードでは、以前の通りに作る訳ですが、metro application も含めて少し考えてみます。なぜ、metro も入れるかというとサスペンドがあるからなんですよね~。

image

まず、データ保持のほうの Model から考えてみます。矩形あるいは三角形などを書くための Point クラスと面を作るための Mesh クラスを準備します。

これをデータとして保存する場合は、XMLでもバイナリデータでも良いのですが、配列な感じで保存します。逆に読み出す時も配列として読み出します。このとき、矩形なり三角形なりの頂点は「すでに出来上がったもの」を保存するのが普通です。

このデータをもとにして、描画をする訳です。便宜上、同じクラス名になっていますが、ここは共有しても良いし、違うクラスにしても良いということです。共有する場合は、一部のプロパティ(statusプロパティとcolorプロパティ)が異なるのでコンバートが必要です。コンバート自体は、View の描画時に判断すると思います。逆にModel-Viewで異なるPointクラスを用意する場合は、Viewでは直接Colorプロパティの値を参照することができます。

// クラスを共有する場合は
View.Color = Point.Color
// クラスを共有しない場合は
View.Color = StatusToColor(Point.Status)

これは、Color 自体の可搬性によって共有するか共有しないかを決めます。あるいは、可搬性を良くする場合は、Status を Color と別ものにすることで、DarkStyle や LightStyle のように色相を変えたときにも対応できるのです。なので少なくとも、Color 自体は共有しないほうが良い、と考えられます。

ただし、共有しない場合は、2つの Point クラスを管理せねばならず結構面倒です。なので、概念的には2種類のPointクラスを扱い、実装的にはひとつのPointクラスを扱う(あるいは自動でコンバートする)ほうが良いと考えらます。

先のファイルへの永続化を考えたとき XML の場合にはクラスでもよいのですが、バイナリファイルとして直接保存する場合は、C++の構造体を使うと便利です。

struct Point {
int x, y, z ;
int status ;
};

class Point {
int x, y, z ;
int status;
};

C#/VB で作る場合には、sturctもclassもあまり代わりがないのですが(実際は、structにするほうがメモリ効率がよいのですが)、C/C++ の場合は構造体のほうが明らかに永続化に関しては便利です。

このような場合、Model 側では、struct Point を使い、View 側では利便性を考えて class Point を使うという方法があります。そして、それぞれのデータを定義するのはメモリ効率的に冗長であるので、class Point では struct のほうを内包させます。

class Point {
struct Point *_pt ;
getX() { return _pt->x ; }
setX( int x ) { _pt->x = x; }

};

この記述は、前回書いた通り、Model の構造に密着し過ぎているために、Model の変更に対して過敏になりすぎます。また、View での変更に対して過敏すぎます。なので、実際には、struct Point と class Point のメモリの冗長性を諦めます。最近のメモリ容量から考えると、二重化させても問題ないでしょうし、既に永続化がされているのですから Model のメモリ領域は解放してしまうことも可能です。これは場合によりけりでしょう。

ただし、Model の Point の構造は View ほど変えないほうが無難です。Model の構造イコール可搬性を意味するので、どのシステム、アーキテクチャに持って行っても「同じ Model の構造を使える」ということは重要です。なので、実際に永続化された構造(ファイルに保存されたバイナリデータ、XMLデータ、データベースの構造)と対になるように Model を作ります。

さて、Model の Point と View の Point のコンバートは静的な構造の変換でしかすぎませんが、Pointの集合体である Mesh は、Model と View とでは随分と違います。

どちらも複数のPointを持つという点では同じなのですが、Viewの場合には「作業途中である中途半端な」Meshデータを保持する必要があります。このMeshデータは、Viewが再描画するときに再利用されます。XAMLの場合には、再描画を直接要求されることは稀なのですが(自前でBitmapを書く場合は別)、Windows アプリケーションを C++で書く、DirectXで書くという場合には、Paintイベント時に発生するためにこれが必要になります。

しかし、Model で持つ Mesh データは、この中途半端なデータを持つ必要があるのか?という疑問が出来ます。簡単に考えると、Modelのデータは「できるだけ欠損してない」データ望ましいでしょう。しかし、Metro Application を作る場合には若干異なるのです。

Metro Application の場合には、ユーザーがアプリケーションをバックグラウンドに置くことができます。と言いますか、画面を切り替えると、表のアプリはバックグラウンドに追いやられてしまうわけです。このバックグラウンドに追いやらられたアプリは、なんらかのタイミングでサスペンド(一時停止)となります。更に、メモリが足りなくなるとサスペンドからアプリは終了に移行させられます。

ユーザーから見ると、バックグラウンドに移動したアプリをもう一度開いたときには「元の状態」に戻ってほしいものです。これは、紙に途中まで書きつけていたメモを、一旦わきに追いやって、もう一度手元に戻したときに「書いていた途中のものが消えない」現実と同じことです。

ですが、これを実現するためには、アプリケーションが一時停止あるいは terminate されたときに、「作業途中の状態」=「バックグラウンドに追いやられた状態」を永続化する必要がでてきます。これは、いままで、「Model は完全なデータである」という習慣とは、かなり違ったものになります。

さて、作業途中の状態は、View の Mesh クラスが持っています。これをそのまま Model の Mesh に移行さて、そのまま永続化すれば「作業途中」の状態が保存されることが分かります。

が、逆に作業途中のデータを「作業途中」の状態へ復元することを考えてみましょう。作業途中である不完全な Mesh データ(1点や直線でしかないデータ)は、永続化された Model の Mesh として復元可能なのですが、そのままでは復元不可能なものが出てきます。

  • View が保持している、カレントの Point 位置
  • View が保持している、視点や回転、描画範囲

これらのデータは、Point は Mesh クラスには表れてこない View 独自のパラメータです。作業途中を復元しようとすると、これらのデータも永続化しないといけない、という難関が待っているのです。

これはメモ帳のような簡単なものであっても、

  • 挿入箇所のカーソル位置
  • スクロール位置
  • リストの表示位置

などがあります。検索途中であれば、検索しているときの文字列も保存しないと復元ができません。となると完全に復元するという現実が、結構困難なものだということがわかります。

さて、iPhone/iPad の場合は、どうなっているのでしょうか?というのを確認してみましょう。実は簡単なルールがあるらしく(apple自体は、この点に関しては明記していないような気がします)、

  • 元に戻すかどうかは、アプリケーション依存
  • 駄目だったら、最初から起動

という動きになっています。いやいや、実に単純明快。これだと途中の Point, Mesh かつ View 独特のデータを永続化しないで済みます。

で、果たして Metro Application の場合はどうなるのかというと、実際のところはよく分かりません。復元せよということにはなっていますが、People などのいくつかの標準機能を途中で落としてみると、スクロール位置までは保管していないようです。まあ、実際に Windows Store が動き出すと、そのあたりの規約もゆるゆるになるかと。

カテゴリー: 開発, windows 8 パーマリンク

グラフィック系のModel-Viewを考える への1件のコメント

  1. masuda のコメント:

    補足しておくと、iOS の場合に復元の動作がばらばらなのは、元がシングルタスクなので他のアプリに切り替えるときには、元のアプリは落ちてしまうという仕様だった、というのが原因。iOS5の場合は、基本メモリに残っているので復元がOS任せというのもある。
    Metro Application の場合は、最初からマルチタスクなのでこういう規約(動作)になっているのだけど、実現が難しいのは確かなのです。中途半端なデータというのは、また別途書く。

コメントは停止中です。