iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い

59,513 views

Published on

iOS/Androidアプリを作る際に理解しておいて欲しい「Model」という役割について説明します。わりと意識していないケースがあるので、チェックしてみてください。

Published in: Engineering
1 Comment
375 Likes
Statistics
Notes
  • すごくうちのチームで議論になったのですが、例えばAからModel経由でデータ取得しました。また、結果が変える前にBからModel経由でデータ取得しました。 ABはIdが違うだけ同じFragmentとかAcitivityです。 この場合BはAのデータを取得して表示されてしまうと思いますが、こんなケースなにかいい解消方法はないでしょうか? ModelのなかでUniqIDみたいなものをもっている必要性が出てくるのではないかと思います。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
59,513
On SlideShare
0
From Embeds
0
Number of Embeds
9,546
Actions
Shares
0
Downloads
319
Comments
1
Likes
375
Embeds 0
No embeds

No notes for slide

iOS/Androidアプリエンジニアが理解すべき「Model」の振る舞い

  1. 1. iOS/Androidアプリエンジニアが   理解すべき「Model」の振る舞い 2014年7月   ゆめみ 森下 健 1 そろそろ   聞き飽きた   煽りタイトルで ごめん
  2. 2. 以前「iOS/Androidアプリの3つの大事な設計方針」とい う話をしたのだけれど、今回はもう少し具体的な設計の 話をするよ 2
  3. 3. この1年で10個近くアプリのコードを見てきたのだけど、 ほぼ全てにおいて「Modelがない」のよ。皆「MVCです。 Modelあります」というけれど、私にはModelが見えな い。そう、「Modelとは何か」が共有できてないのよ 3
  4. 4. Modelという言葉は色々な意味で皆捉えるから、本当 は別の良い言葉を使えれば良いのだけど、見つからな いのよね。誰かエロイ人が決めてくれればなぁ。 4
  5. 5. 以前の資料では「UIではない部分≒Model」としたけど、 今回はもう少し具体的に構造を整理して「(よく忘れら れている)Modelの重要な振る舞いは何か?」について 考察するよ。 Modelって俺の ことさ 大喜利どうぞ 5
  6. 6. まず、結論から述べよう!それは次の2点!   •  基本死なない   •  通知で変更を伝える   この2点がなぜ重要かをこれから説明するよ   6
  7. 7. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   この図はアプリを作る時によく出てくるコンポーネントよ。 ここではコンポーネントという言葉を、「一塊の機能を実 現するClass群」という意味で使っているわ。(他にいい 言葉無いかな…) よく使うコンポーネント 外部サービス データソース コンポーネント コンポーネント コンポーネント コンポーネント コンポーネント コンポーネント コンポーネント 7
  8. 8. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Modelもコンポーネントの一つよ。まずは、他のコン ポーネントとModelの違いをはっきりさせることにするよ。 よく使うコンポーネント 8
  9. 9. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   まずはプレゼンテーション層から。   9
  10. 10. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Viewは「データを表示」したり「ユーザの操作を受け付 ける」部分ね。Class名に  〜View  となっているやつかな。 ViewというよりWidgetみたいなものだけど。ImageView とかBuLonとかね。   ボタンやTextbox等 EventをTargetに送る データを表示する   10
  11. 11. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   次は、iOSならViewController、AndroidならAc@vityや Fragmentと呼ばれる部分ね。iOS的な呼び名で悪いけ ど、今後VCと呼ぶことにするわ。古典MVCでいうViewと いうのはこのVCのことだと思うけど、論争になるので略   ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 1画面1+インスタンス ボタンやTextbox等 EventをTargetに送る データを表示する   UIの状態制御とModelとの対話 11
  12. 12. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   VCの役割は画面の制御。Viewとの対話、画面の状態 制御、Modelとの対話、が主な役割ね。複雑なビジネス ロジックが入り込みがちなのは世の常だけど、ある程 度経験がある人はそれを意識して回避するわね。   ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 1画面1+インスタンス ボタンやTextbox等 EventをTargetに送る データを表示する   UIの状態制御とModelとの対話 フルスタックVCはよく あるアンチパターン 12
  13. 13. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   VCの特性として忘れてはいけないのは、「ユーザの操 作で生成・消滅する」ということよ。それがいつなのか 基本的に予測が付かないわ。これ重要だから覚えてお いてね。あと、Singletonにするとかやめてね。   ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 1画面1+インスタンス ボタンやTextbox等 EventをTargetに送る データを表示する   UIの状態制御とModelとの対話 トキ、安らかに眠れ 13
  14. 14. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   次はビジネス層。   14
  15. 15. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   まず、ビジネス層はプレゼンテーション層を参照しては いけないよ。これは絶対のルール。   ✕ ダメ、絶対 15
  16. 16. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   En@tyは「データそのもの」よ。他のModelやAPIを参照 したり、そういう複雑な操作は一切しないわ。単にList やMapだったり、DTOやValueObjectみたいなシンプル なObjectをイメージしてね。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer 16
  17. 17. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   でも例えば「画面の範囲」を表すEn@tyで「面積を計算」 「中央の位置を計算」程度なら良いと思う。でもGlobal なPropertyを参照したりはしない。さじ加減は難しいけ どあまり他のClassを参照することはしないかな。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer 17
  18. 18. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   En@tyはアプリケーションの本質となるデータで、フラグ とか画面・通信状態とかとはまた違います。ユーザが 入力したり、サーバから取得したようなデータね。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer 18
  19. 19. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   En@tyはマルチスレッドの場合などで、不変Objectであ る方が便利なこともあるけど、そこまで意識しなくても 良いかもしれない。スレッド周りの罠はたくさんあるから 今回は触れないようにします。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer スレッドはパンドラの箱 19
  20. 20. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   En@tyがSaveとかLoadとかデータ層へのアクセスを提供 するケースもあります。でも、オススメはそういうことは Modelに任せてしまって、En@tyは単なるデータくらいの 位置づけが良いと思っています。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer 色々あるからなー 20
  21. 21. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   さて、いよいよModel。Modelはデータを取得・提供・計 算・保存するのが主な役割。データを扱う中心的な役 割ね。内部状態も持つよ。Modelは複数存在して各々 取り扱うデータが異なるよ。ここは概ね良いわよね。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 21
  22. 22. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Modelは、基本Singleton的な非常に長寿命なObject、 とするわ。「基本死なない」ということ。Sta@cなロジック やHelper的なものは除くけど。これはいつ消滅するか わからないVCやAPIと大きく違う点になるわ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 22
  23. 23. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Modelは、データの更新があれば「通知」で変更を伝え る、とするよ。「通知」は「Callback的なもの」とは少し意 味合いが違うよ。これは後でまた説明します。ちなみに ObserverとかNSNo@fica@onは「通知」の実装例だよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 23
  24. 24. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   この2点「基本死なない」「通知で変更を伝える」という ものをModelとした場合、「Modelがない」ことが多い、と いうのが最初に言っていたことになるわ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 24
  25. 25. (VC)   ViewController   Fragment View View En@ty En@ty En@ty En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   要するに、大抵こういう構成になっているイメージね。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データ処理   ロジック   25
  26. 26. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   「基本死なない」「通知で変更を伝える」という性質が何 故大事なのか、というのは後でまた説明するよ。実は そんなもの必要ないかもしれないよね。でも、これが無 いから落ちたりグダグダな実装は多いんだよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 後ほど 26
  27. 27. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   とりあえず残りのコンポーネントの説明をしておくよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 27
  28. 28. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Model  Locatorは、Modelを保持しておくだけのコンポー ネント。「Service  Locatorパターン」というのがあるけど、 その名前をちょっと借りたの。適切な名前かはよくわか らないけど…   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 死なない Modelは誰かが登録する   自分では登録しない Modelを返すのが仕事 名前むずい 28
  29. 29. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   LocatorはSingletonで「ModelのSeLer/GeLerしかない」 と思って良いよ。Setはアプリの起動処理時にしかるべ き部分で明示的にModel  Objectを作成してSetするよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 死なない Modelは誰かが登録する   自分では登録しない Modelを返すのが仕事 29
  30. 30. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   起動時に明示的にSetするメリットはあって「設定(環境 情報)を伴うModelの初期化」「Main  Thread以外だと初 期化できないModelの初期化」が自然に扱えるというと ころかな。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 死なない Modelは誰かが登録する   自分では登録しない Modelを返すのが仕事 Singletonだと少し辛い 30
  31. 31. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   他にも、ModelはSingletonで実装しても良いのだけど 「Singletonだと単体テストでTest  Doubleに置換しにく い」のを解決するし、「Pluginのような実装が後から ModelのObjectを置換できる」というメリットもあるよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 死なない Modelは誰かが登録する   自分では登録しない Modelを返すのが仕事 挿げ替え簡単 31
  32. 32. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   Model  Locatorを使うかどうかは状況次第だけど、さっ き言った問題で困ったら使うと良いよ。   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object 不変ObjectがBeLer データの提供・取得・更新の責任者 機能やデータグループ毎に存在 基本死なない 「通知」で変更を伝える 死なない Modelは誰かが登録する   自分では登録しない Modelを返すのが仕事 32
  33. 33. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   最後にデータ層。   33
  34. 34. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   まず、プレゼンテーション層参照することは絶対にダメ。   ビジネス層も参照しないのが原則だけど、「APIの結果 でEn@ty  Objectを返す」のをどう考えるべきか…  密結合 で替えが効かないけど、議論の余地はあるのかも?   ✕ ダメ、絶対 ? 34
  35. 35. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   APIは、外部サービスの通信の仕様を吸収するよ。1リ クエスト1インスタンスの短命さ、他のAPIを呼び出さな い単純さ、を原則とすると良いと思うよ。   短命単純 1リクエスト1インスタンス 外部サービスの通信仕様を吸収 APIは他のAPIを使用しない   やっぱり単純は最高だぜ 35
  36. 36. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   さっきも触れたけど、APIの結果としてビジネス層の En@ty  Class  Instanceを返すのをみたことがあります。密 結合になる、というのがデメリットなんだけど、En@tyが APIと1対1ならありなのかな、と思わなくもない   短命単純 1リクエスト1インスタンス 外部サービスの通信仕様を吸収 APIは他のAPIを使用しない   何か歪だ 36
  37. 37. (VC)   ViewController   Fragment View View Model   En@ty En@ty En@ty Model  Locator   Model   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   最後、Local  Data  Accessは、ファイルやDBなどへのアク セスを提供するよ。同期的な場合と非同期的な場合が あるかな。ここも色々なケースがありそうだし今回はあ まり触れないことにします。   短命単純 1リクエスト1インスタンス 外部サービスの通信仕様を吸収 APIは他のAPIを使用しない   ファイル・DBなどへのアクセス   37
  38. 38. これからは   「Callbackと通知」   「なぜModelが必要なのか」   を説明していくよ!   38
  39. 39. Callbackは、ニュアンスとして   「(自分と相手が)基本1対1の関係」   「1回呼び出すと1回呼び返される(callback)」   という意味で使うよ。   1回呼ぶと   1回返ってくる 1対1 Callback 39
  40. 40. 通知は、ニュアンスとして   「(自分と相手が)基本1対多の関係」   「呼んでも来ない呼ばなくても来る、ことがある」   という意味で使うよ。   呼んでも来ない   呼ばなくても来る 1対0...N   通知 誰もいないこともある たくさん相手がいることもある 呼ばなくても来る 呼んでも来ない 40
  41. 41. Callbackは「(iOSの)Blocks/delegate」や「(Javaの)無名ク ラス/Interface実装」や「Deferred-­‐Promise」を使ってよく 実装されるね。Promiseの場合「1対1の関係」では無い けど、とりあえずこっちに分類しておくよ。   [[[API  create:  productId]  onFinish:  ^(Result  *result)  {          //  結果の処理   }]  execute]; new  API(productId).onFinish(new  Finish()  {                            @Override                          public  void  onFinish(Result  result)  {                                  //  結果の処理 }                  }).execute(); Blocks 無名   クラス Callback 41
  42. 42. 通知は「(iOS)NSNo@fica@onCenter/Key  Value   Observing(KVO)」「(iOS)delegate」「(Android)Handler」や 「Observerパターン」を使ってよく実装されるね。 delegateの場合「1対多の関係」では無いけどね。   NSNo@fica@onCenter Observer   パターン   通知 42
  43. 43. Callbackでよく使われる「Blocks」「無名クラス」 「Deferred-­‐Promise」で(通常)共通するのは、「呼び出し 側が相手に参照を掴まれていて解除することができな い」ということよ。これは実装方式による特徴ね。   呼び出し側 呼び出される側 Call Callback Blocks、無名クラス、Deferredでは   ここを断ち切る手段がない Callbackでよくある実装 43
  44. 44. 通知でよく使われる実装は、「通知を受け取ることを解 除する」方法が大抵あるわ。従って、「参照を掴まれて 放されない」という問題を回避する手段があるというこ とね。   通知受け取り側 通知する側 通知 通知を受け取ることを解除できる 通知受け取り側 通知受け取り側 通知 44 (Handlerだと無いかな?)
  45. 45. 違いをまとめるとこうなるよ。この定義が一般的かはよ くわからないけど、今回の話の中ではこういう違いを意 識してね。通知は実装が少し面倒だけど「解除手段が ある」「1対多もOK」なのが重要だよ。   Callback  的 通知 相手との関係 1対1 1対多 Callback/通知回数 1回呼ぶと1回Callback 基本、不定   (だと思っておく) 実装方法 Blocks/無名クラス/Deferred   (↑この3つはクロージャ系)   Delegate/Interface Observerパターン   NSNo@fica@onCenter/KVO   (Delegate/Interface) 明示的な解除手段 クロージャ系だと普通はない 普通はある 45
  46. 46. 次に「なぜModelが必要なのか」、つまり、   「基本死なない」   「通知で変更を伝える」   がなぜ必要なのか説明するよ!   46
  47. 47. まず、Modelが無い状態でどういう問題が起こりえるか を考えるよ。   (VC)   ViewController   Fragment View View En@ty En@ty En@ty En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   データ処理   ロジック   ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 短命単純 1リクエスト1インスタンス 想像する 47
  48. 48. こういう場合、大抵VCがAPIを生成・所有することになる ね。また、APIからVCはCallback的になることが多いわ。   (VC)   ViewController   Fragment View View En@ty En@ty En@ty En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   データ処理   ロジック   短命単純 1リクエスト1インスタンス ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 生成・所有する Callback 48
  49. 49. VCはAPI  Callをした直後に消滅を始めて、Callbackがあ る頃にはDestroyされているけどInstance自体は生きて いる(APIが掴んでいるから)ことがある   (VC)   ViewController   Fragment View View En@ty En@ty En@ty En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   データ処理   ロジック   短命単純 1リクエスト1インスタンス ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 生成・所有する Callback 49
  50. 50. 例えばAndroidだとDestroyされたVCで、Dialogなど出そ うとすればそれで落ちるわ。VCが連続でAPIを呼び出す 実装だと無駄な処理が継続することになるね。こんな 風に「危険」と「無駄」の温床になるということね。   (VC)   ViewController   Fragment View View En@ty En@ty En@ty En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   データ処理   ロジック   短命単純 1リクエスト1インスタンス ユーザ入力やデータ 層の結果から生成 データそのもの List,  Map,  Object ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 生成・所有する Callback やらなくて良い   Callbackのコードが動く 既に無効なVC 要らん事しないで 50
  51. 51. また、ユーザが何度もそのVCを生成・消滅させるとそ の度にAPI呼び出しとCallback処理が実行されることに なる。このせいで「画面を行ったり来たりしていると重く なる(果てには落ちる)」原因になるよ。   (VC)   ViewController   Fragment API   短命単純 1リクエスト1インスタンス ユーザの操作 (VC)   ViewController   Fragment API   (VC)   ViewController   Fragment API   生成・消滅 生成・消滅 生成 ユーザの操作で   生成・消滅 ボタン連打などで   頻繁に生成・消滅もあり得る 死ねばいいのに 通信   通信   通信   連打に負けた 51
  52. 52. •  「危険」と「無駄」の温床   •  「画面を行ったり来たりしていると重くなる」   この2点が主要な「問題」よ   52
  53. 53. (VC)   ViewController   Fragment View View Model   Model  Locator   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   そこでModelを使うよ。こんな風になるよ。   生成・所有する Callback 通知 所有 更新要求 基本死なない 「通知」で変更を伝える 53
  54. 54. VCは次の手順で動くよ。   •  通知を受け取る準備をしてModelに「要求」を伝える   •  消滅する前にModelからの通知受信を解除する   (VC)   ViewController   Fragment onCreate()  {      this.model  =  ModelLocator.getXXModel();      this.model.addObserver(this);      ...   }     updateMyView()  {      XXEn@ty  xx  =  this.model.getXX();      if  (xx  ==  null)  {            this.model.fetchData();      }  else  {      ...     }     onDestroy()  {      this.model.removeObserver(this);   } こんな感じ 54
  55. 55. (VC)   ViewController   Fragment View View Model   Model  Locator   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   まず、VCは無効になる前に「通知の受信を解除」する ので、前述の「危険と無駄の温床」はなくなるよ。   生成・所有する Callback 通知 所有 更新要求 基本死なない 「通知」で変更を伝える 既に無効なVC 解除する 帰宅するときは   社用携帯  OFF! 55
  56. 56. (VC)   ViewController   Fragment View View Model   Model  Locator   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   また、ユーザの操作で何度もVCが生成されて、Model に何度も「更新要求」を出しても、Modelが内部状態を 持って無視するようにできるよ。「既に通信中」「X秒前 に通信したから無視」のようにね。   生成・所有する Callback 通知 所有 更新要求 基本死なない 「通知」で変更を伝える 状態によって   要求を無視 やり過ごし社員! 56
  57. 57. これで   •  「危険」と「無駄」の温床   •  「画面を行ったり来たりしていると重くなる」   この問題が解消するよ   57
  58. 58. VCとModelの呼び出しはこんな感じになるよ。 fetchPoint()みたいなのを明示的に呼ぶかは色々ある かな。その辺はModelの挙動ポリシーかな。   MMooddeell VVCC 残高教えて→ ←ごめん、わからん じゃあ調べてよ→ ←通知 残高教えて→ ←1000pt  だよ 通信中 model.getPoint()     -­‐>  null model.fetchPoint() model.getPoint()   -­‐>  1000 (通信しないかもしれない) 決めの問題かな? 58
  59. 59. 他にもModelは通知の仕組みがあるから、異なるVCに 即座に変更を伝えることができるよ。Excelみたいに、 「シートの値を変えると複数のグラフ表示が変わる」よ うなこともできるよ。   (VC)   ViewController   Fragment Model   Model  Locator   En@ty En@ty En@ty API   API   API   プレゼンテーション層 ビジネス層 データ層 State  Machine Local  Data   Access   生成・所有する 通知 基本死なない 「通知」で変更を伝える (VC)   ViewController   Fragment MVCが綺麗にハマるね 59
  60. 60. Excelなんて作らないよ、って思うかもしれないけど、 メールの未読数と未読マークも一つのModelを複数VC が参照するケースだよね。こういう場合もModelから通 知で考えておくと自然に作れるよ。   未読件数表示 未読マーク .   戻った時に更新されて欲しい 未読更新通知 データは一つ 60
  61. 61. まとめると、今回定義したModelでこうなるよ   •  危険と無駄の温床、が解決   •  画面を行ったり来たりしていると重くなる、が解決   •  VC間でデータの共有や表示の更新が簡単になる   61
  62. 62. いくつかこの設計に関する議論を紹介するよ   62
  63. 63. 開発時は大抵高速回線でアプリを動かすから、気が付 きにくいというのもあるのよね   議論 API通信なんて1,2秒で終わるから、   そこまで神経質にならなくても   良いのではないか? Mobileだと通信環境が悪いときも多いし、   その場合かなり時間かかるかな。   ユーザもイライラすると連打したりするし。 63
  64. 64. VCがGCされない、ことだけが問題ではないということか な。VCが「終わった、リソース解放!」した後のCallback が危ないんだよ   議論 明示的に通知を解除しなくても   Model側がWeakRef(弱参照)で   VC側を掴んでおけば問題ないんじゃないか? 確かに弱参照は推奨されることが多いね。   でも、AndroidでDestroyからInstanceが破棄さ れるまでの間にCallbackが発生すれば問題 は同じだよ 64
  65. 65. フラグが乱立するのは死亡フラグ   議論 通知を解除とかしなくても、無効flag立てて   Callback内で無効flagをチェックするとかでも   良いのではないか? 参照を掴まれている間はVCは消滅しないか らメモリの解放が遅れるけど、そういうのが大 丈夫なら大きな問題はないかもね。でも、そう いうFlagって美しくないなぁ。忘れそうだし。 フラグを折れる人   になりたい 65
  66. 66. cancel()は通知を解除しつつ、さらにcancel()というよう に使うと良いよ。   議論 VCからcancel()みたいなのを送れれば良いので はないか? 確かにcancel()が有効なケースもあるね。特 にcancel()によってキューイングされている未 実行の重たい処理が破棄できる場合とかね。 66
  67. 67. 構造的に潜在的なリスクがあるのは怖いよね   議論 Callback処理が   たいして無駄でも危険でもない場合は   問題ないのではないか? 今は危険ではなくても、将来誰かが「問題に なる処理」を付け足す可能性があるわ。そう いう地雷は無い方が良いと思うの。 67
  68. 68. UIの快適さはNa@veアプリの大事な強みだしね   議論 通信中なら画面遷移させない・VC破棄させない   とか、そういう考えで良いんじゃないか? 通信中だろうがユーザの操作はなるべく妨げ 無い方が良いよね?それって最後の手段だ と思うんだ。 68
  69. 69. 志を下げないで   議論 連打するのはユーザが悪い、とか、   そういう考えで良いんじゃないか? ユーザは悪くないよ。可能な操作をしている のに落ちたり重くなる方が悪いよね。Excelと かPPTが落ちたらその人のせいなの? 69
  70. 70. 「今回のModel定義」ってサーバサイド開発をやってき た人には馴染みがなくて抵抗があるんじゃないかと思 うの。私も実はその口なのよ。でも、クライアントアプリ をやってきた人には普通らしいんだよね。   余談 70
  71. 71. サーバサイドのアプリだと、「リクエストが来てレスポン スを返す」というのが鉄板の流れだよね。   サーバ リクエスト Controller Model DAO レスポンス PHPとか 71
  72. 72. この場合、各層の静的な参照構造の依存関係をなくし たり、操作を抽象化しておけば、結構良い感じになるよ ね。   Controller Model DAO 抽象化  依存性注入 サーバ 72
  73. 73. クライアントアプリだと、それぞれの層に予測しにくいタ イミングでイベントが発生するね。特にユーザの操作は 読めないわ。   VC Model DAO ユーザの操作 APIレスポンス センサーデバイス 出入口多数 クライアント 73
  74. 74. なんかもう住んでる世界が違う感じだと思うの。だから クライアントアプリの場合は、VC-­‐Model間はタイミング 的な依存関係も無いようなイメージで作る方が上手くい く気がするの。   Model Ac@ve VC Ac@veなVCは移り変わる・指先一つで死ぬ 長寿・マイペース APIレスポンス センサーデバイス ユーザの操作 ユーザの操作 74
  75. 75. 呼んだら呼ばれる、ではなく、お互い好き勝手にメッ セージを送り合うような。それで問題なく動くように作る べきかと。そのために互いに状態を持ち、メッセージへ の反応には自己責任で対応する感じかな!   Model Ac@ve VC Ac@veなVCは移り変わる・指先一つで死ぬ 長寿・マイペース APIレスポンス センサーデバイス ユーザの操作 ユーザの操作 タイミング的にも緩い結合 独自の「時」 を刻む 独自の「時」 を刻む 75
  76. 76. 今のは私の脳内イメージの余談だったけど、クライアン トアプリケーションの性質を把握した上で設計を検討す ると良いと思うよ。   76
  77. 77. 77
  78. 78. 参考 •  AAfN:  アプリケーションとサービスの設計   –  hLp://msdn.microsoq.com/ja-­‐jp/library/ms978348.aspx 78

×