タグ: アーキテクチャ

ReSwiftのサンプルコードの仕組みをRxSwiftで実現する

 ReSwiftのサンプルコード程度であれば、RxSwiftの機能を使って自前で実装ができてしまうのでは、と考えたので実際にやってみました。不必要に依存ライブラリを増やしたくないという気持ちがモチベーションとなっています。まずはReSwiftのサンプルコード的なものを確認するところから始めます。

 ちなみにこの記事は ReSwift を使い始めて数時間の人間が書いているので、間違っている可能性が大いにあります。嘘を嘘と見抜きながら読んでいただけると幸いです(そして、コメント欄で刺してください)。

ReSwiftとは?

 ReduxのSwift実装らしいです。そもそもReduxがよくわからないのですが、そんなものわかってなくても動くコードはかけそうでした。ということで、とりあえずコードを書いて読んでから、小難しい記事を読んで理解してく方針にして、Redux および ReSwift の解説は他の記事に譲ります。

簡単な数字のカウントアップ・アプリケーション

 まずはReSwiftを用いてどのようにアプリケーションを構成していくか、ごくごく簡単な数字のカウントアップ・アプリケーションの例を用いてみていきましょう(ただし実際にはこんな単純すぎるアプリケーションに小難しいアーキテクチャを採用するメリットはないと思っています、あくまで例示としてのお話です)。まず CocoaPods でも Carthage でもなんでもいいので適当に ReSwift を導入してください。

 ReSwiftではアプリケーションの持つ状態をStateTypeプロトコルを持つ構造体 or クラスにまとめるようです。

 つづいて Action の宣言です。今回のアプリケーションに発生しうるアクションとして数字をカウントアップするアクションとカウントダウンするアクションを宣言してみます。

 簡単ですね?さて続いては、アクションを解釈して、新しい状態を発行する役割を持つ Reducer を実装してみましょう。状態を変化させるのではなく 新しい状態を発行する という点に注意しながらコードをみてみると良いと思います。

 そんなに難しいものではなく、action がもし、CountActionIncrese 型だったら、現在の状態のcounterをカウントアップしたものを生成して返すというだけです。Decrease も同様に考えれば同じようなものですね。

 最後に皆さんおなじみの ViewController を実装してみましょう。まずは、ストーリーボードに適当にラベルとボタンを配置してください。

スクリーンショット 2016-02-12 7.12.03

 つづいて、ViewController に label と ボタンのアクションを紐づけて、以下のようなコードを記述します。

 各アクションで文字通り Action を発行しているだけで、ラベルのテキストを書き換えたりカウンターの状態(数字)を書き換えたりしていない点が非常に重要なポイントです。アプリケーションの状態の書き換えは、Reducerで行います。そして、ビューの書き換えは newState で行います。こうすることにより、状態遷移(実際には新しいステートの発行)の記述と、ビューへの反映を分離しています。

 以上のような簡単なアプリケーションだとしても、多分コードみただけじゃ、感覚はつかめないのではないでしょうか?簡単なのでXcode起動して、ちゃちゃっと実験してみてください。5分で終わるので絶対自分の手でコードを書いてみてください。

RxSwiftを使って、上述の形式のコードを実現する

 細かいことは抜きにして、RxSwiftを使って同じ動きをエミュレートしてみましょう。まずは state, action, reducer の定義から。

 ほとんど変化はないですね?次に ViewController は次のような感じ。ちょっと記述は長くなりましたが、だいたい読み替えは可能なのではないでしょうか?

 もちろん、厳密なお話をするとおなじものにはなっていないのですが、ちゃんとプロトコルと基底となるコードをちょっと書いてあげれば、たちまち同じようなものになるのではないでしょうか?(自分の理解が間違っていたらご指摘をお願いします:ReSwiftを触ってまだ1日目です)

おわりに

 楽なので、よくやってしまいがちなのは、ViewControllerに var で状態を宣言して、イベント処理時に変化させていくという感じの実装ではないでしょうか?このサンプルレベルの簡単なアプリケーションであれば、その実装は正しいと思います。シンプルでわかりやすいので、別にそれで全然かまわないと思います。

 しかし状態の数が増えると、結果としてアプリケーションの取りうる状態の数は、各プロパティの取りうる状態の組み合わせ(高校で習った数学的なお話)になります。状態が10個程度を超えてくると、もうアプリケーションコードを読むのが辛くなってきたり、一つの変更が別の箇所に影響してしまい、意図しない動作を引き起こしてしまうなどということがあるのではないでしょうか。

 自分もまだ ReSwiftRedux がどういうものがわかっていませんが、状態の遷移とビューへの反映の混在を分離するところから、まずはじめてみようと思っています。

MVC再考 (1)

どの文献を参照しても共通していること

  • ユーザーインターフェースを持つアプリケーションを Model/View/Controller の3つにの部分に分離する

解釈が難しい点

  • モデル・ビュー・コントローラの責務と関係
    • 特にコントローラの責務
    • コントローラとビューの関係性
  • 各プラットフォームにおいて、上記の責務をどう実装に落とし込むのか
    • ほんとうにビューとコントローラは分離できるのか?
    • そもそもビューとコントローラを分離する利点は?

分離とは?

Model-View-Controller に分離するパターンを見ていくが、クラスに分割するだけが分離に相当するわけではない

  • サブルーチン・メソッド
  • クラス
  • パッケージ
  • モジュール

MSDNのMVC記事まとめ

MSDNの記事がわかりやすかったのでまとめた

背景

  • コンピュータシステムの多くは データ を取ってきて、 ユーザ に表示する
  • ユーザがデータを変更したら、 データストア の内容を更新することもある

そのため、データストアとUI間のデータの流れに目がいきがちであり、パフォーマンスのため、またはコード量を減らすために、しばしばその処理を密結合にしてしまうことがある。

このアプローチには2つ問題点がある。

  • UIはデータストアと比較して頻繁に変更が入る傾向にある
  • 複雑なアプリケーションでは、単にデータストアからデータを送受信するだけでなく、複雑なビジネスロジックが存在する傾向にある

問題

ドメインロジックとUIの変更をそれぞれ手軽に独立して行えるようにするためには、Webアプリケーションをどのようにモジュール化すれば良いのだろうか

前提

問題を解決する際に、次のようなことを考慮する必要がある

  • UIロジックはビジネスロジックに比べて頻繁に変更が入る傾向に有る
    • 特にWebアプリケーションにおいては顕著
    • 例えば、新しい画面が追加されたり、すでに存在するページのレイアウトが変更されたりすることが多い
    • したがって、サーバー側に処理をまとめたシステム(シンクライアントシステム)は、アプリケーションを再配布しなくて済む
    • プレゼンテーションコードとビジネスロジックがひとつのオブジェクトとして結合している場合、UIだけを変更したくてもビジネスロジックを含んだオブジェクトを変更しなければならない
    • これはUIの些細な変更であっても、エラーを誘発したり、再テストが必要だったりすることを意味する。
  • 場合によっては、アプリケーションは同じデータを異なる形で表示することがある
    • 例えば、アナリストはスプレットシートを好み、マネージャは円グラフを望むことがある
    • またそれらを同時に表示することも十分に考えられる
    • もしユーザがデータに変更を加えた場合、すべてのビューに反映されるべき
  • デザインと複雑なビジネスロジックを作る作業は全く異なるスキルであり、両方のスキルを持ち合わせる人は滅多にいない
    • そこで、これらの作業はバラバラに実行できることが望ましい。
  • プレゼンテーションはデータソースからデータを取得し、データをフォーマットして表示する
    • 表示されたデータに応じてユーザーはなんらかのアクションを行い、そこからうまれた変更を反映させるためにビジネスロジックへ制御が渡る
  • WebアプリケーションはHTTPのステートレス性の上で、異なるアクションを実現しなければならない
  • UI層のコードは、ビジネスロジックと比較して、デバイスに依存する傾向にある
    • したがって、もしプラウザベースのアプリケーションからモバイル端末などに移植したい場合はUI層のコードを置き換える必要がでてくる
    • UIに関するコードとビジネスロジックを分離しておくことは、移植を素早く行えるようにしたり、移植後のビジネスロジック層のエラーを減らしたりすることに役立つ
  • UI層のテストは難しくて、実行に時間がかかる傾向にある。したがってUI層と直接結びついているコードを減らすことはテスタビリティ向上につながる。

解決策

Model-View-Controllerパターンは、ドメインモデル、プレゼンテーション、ユーザーからの入力に対するアクションの3つのパートに分離をする[Burbeck92]。

  • Model: モデルは状態に応じたアプリケーションドメインの振る舞いを扱い、主にControllerからの操作に応じて状態を変化させる。
  • View: ビューは表示される情報を扱う
  • Controller: マウスやキーボードなどのユーザからの操作を解釈して、モデルやビューに適切に伝える

mvc

  • ビューとコントローラーはモデルに依存してよい
  • モデルはどちらにも依存してはいけない
    • これにより、モデル層とプレゼンテーション層のビルドやテストをバラバラに行うことができるようになる
  • ビューとコントローラーの分離はリッチクライアントアプリケーションでは二の次となる
    • 実際のところ多くのUIフレームワークではひとつのオブジェクトにビューとコントローラーの責務をおいているものが多い
    • 一方、Webアプリケーションにおいてはビュー(ブラウザ)とコントローラー(HTTPリクエストをさばくサーバーサイドコンポーネント)は明確に分かれている。

Model-View-Controller はUIロジックとビジネスロジックを分離するための基本的なデザインパターンである。有名であるがゆえに、多くの間違った説明がなされている。

特に Controller は色々な文脈で異なる説明がなされている。ただし、Webアプリケーションにおいてはビューとコントローラーは明確に分かれているために、こういった曖昧さを解消するためには丁度良い。

passive型 MVC

Steve Burbeckは、In Application Programming in Smalltalk-80: How to use Model-View-Controllerの中で passive 型と active 型の2種類の MVC のかたちについて説明している。

passive型MVCはあるコントローラがモデルを排他的に制御するときに用いることができる。コントローラはモデルを変更し、ビューにモデルが変更されたので内容を更新する必要があることを伝える。このやり方だと、モデルはビューやコントローラと完全に独立した形になり、モデルはビューに変更を伝える手段がない状態になる。HTTPプロトコルなどがこのパターンに合致する。ブラウザがサーバーから更新を受け取るための、この上なくシンプルな方法である。ブラウザはユーザからの入力に応じてビューを表示するが、サーバー上のデータの変化を検出することはできない。ユーザが明示的に更新を問い合わせる必要が有る。

passive

active型 MVC

active 型はコントローラが関わらなくてもモデルの状態が変化する際に用いられる。このモデルの変化は他の入力ソースからデータの変更が発生してしまう場合に起こりうる。基本的にはその変更はビューに反映されるべきである。電光掲示板を作るときにはこうしなければならないだろう。表示内容に変更があった場合、データ取ってきてビューに反映させる必要がありそうである。

しかし、MVCパターンを用いる大きな目的のひとつに、モデルとビューを分離するというものがあったことを思い出そう。モデルがビューに変更を通知するとなると、ふつうにやれば依存関係が発生してしまう。そこでGoFのObserverパターンを思い出そう。これはオブジェクト同士の依存を避けつつも、変更を通知することを実現するデザインパターンである。それぞれのビューはObservarインターフェースを実装して、モデルを購読する。モデルが変更されたら、登録されているすべての observer に通知を行うという仕組みになる。このやり方はしばしば、publish-subscribe と呼ばれる。モデルはビューに関する情報は一切必要としない。たとえば、メニューのオプションを使えなくする必要があるなど、コントローラーがモデルの変更通知を必要とする場合、すべてのコントローラはobserverインターフェースを実装する必要がある。また、たくさんのビューがある場合、それぞれ異なる領域ごとにモデルを定義し、各ビューは関心のあるモデルのみを subscribe すると良いでしょう。

mvc

activemvc

もたらされる影響

MVCのもたらす恩恵

  • 複数のビューをサポートできる: モデルとビューが分離されているため、モデルからビューへ直接の参照は存在しない。したがって、同時に同じデータを様々なビューで表示することができる。これはWebアプリケーションにおける複数のページは同じモデルオブジェクトを使っていることがあることからもわかるだろう。またユーザがページの見た目を変更できるようなWebアプリケーションの存在からも理解することができるだろ。そういったページは同じモデルから得られた同じデータを、異なる方法で表示することができている。
  • 変更に強くなる: UIはビジネスロジックと比較して変更が生じやすい。ユーザは異なる配色やフォントやレイアウトを好むかもしれないし、デバイスに特化したインターフェースを望むかもしれない。モデルがビューに依存していないので、新しいビューのセットをシステムに追加しても、モデルは何の影響も受けない。結果として、変更の影響はビューだけに限られる。この部分をさらに細分化したパターンとして Page Controller と Front Controller というパターンがある。

MVCのもたらす弊害

  • 複雑になる: MVCパターンは遠回りなやり方であり、システムを複雑にする。またデバッグの難しいイベント駆動なコードベースになる傾向にある。
  • 頻繁な更新に対するコストが大きい: モデルとビューを切り離すことは重要だが、決してモデルがビューの存在を無視してよいということではない。モデルが頻繁に更新を行うような場合、ビューにそれを反映させるコストは大きなものとなるだろう。特にグラフィカルなユーザーインターフェースにおいては顕著になる。このような10教科ではビューは描画に失敗することもあるだろう。したがってモデルを作成するときにはビューを念頭に置くことは重要だ。場合によってはモデルの複数の変更をまとめてビューに通知するなどということも必要なはずである。

Document-View パターン

Model-View-Controllerの責務分離は行ったまま、ViewとControllerを結合 したものを Document-View パターンとよんでいる。今日の、多くのGUIプラットフォームではこのパターンが用いられている。VC++のMFCなどはこのパターンの実例のひとつになる。

単一責任の原則(SRP)

Single Responsibility Principle(単一責任の原則)は、Tom DeMarco の “Structured Analysis and Systems Specification” と、Meilir Page-Jones の “The Practical Guide to Structured Systems Design” で説明されている。この原則は次のようなものだ。

クラスを変更する理由は複数存在してはいけない

なぜこうする必要があるのか?

 役割が単一なクラスの仕様要求が変化した場合、その変更部分は浮き彫りになり、どのように変化したのかわかりやすい。これは、ほとんどの人が納得するはなしではないだろうか。

 しかし、役割が複数あると、その1つ1つが変更理由になってしまう。複数の変更理由によってクラスが変更されると、変更部分がぼやけてしまう。また、ある理由により変更した部分が、他の役割に影響してしまい、連鎖的に変更が必要になるなどといったケースが生じうる。これは もろい設計 にあてはまる。もろい設計がソフトウェア開発にどんな影響を及ぼすかは、前に書いた記事を参照されたい。

役割とはなにか?

 「役割=変更理由」と定義している。この判断が非常に難しいケースも多々有る。

変更の理由が変更の理由たるのは、実際に変更の理由が生じた場合だけである

 変化の兆候がないのにSRPを含めた原則を適用するのは賢明ではない。原則を適用することを目的にしちゃいけないというのは、いつも気に止めておかなければならないなぁと思います。

結合している役割を見つけそれらを分離する作業は、ソフトウェア設計の本質である

 非常に身にしみる言葉。

どのように分離するのか

たとえば、次のようなモデムインターフェースを考える。

 最初の2つのメソッドは接続の開始と終了、すなわち接続管理の機能を持つ。後半の2つのメソッドはメッセージのやりとりに用いる。この2つの役割を分離すべきかどうかは今後このアプリケーションがどのように変更されるか次第。たとえば接続管理の機能が影響を受けるような変更がはいる場合は、sendrecv のみを呼び出しているクラスをリコンパイル、リロードしなおさなければならない。もっとも単純にこの状態から打破するには、以下のように分離すればよい。ModelImplはその場しのぎではあるが、すべての依存関係が解消しているはずだ。

oymhIIrAIqnELN19B4bqpaZCoqlDKQZcqbOeJirBqR1ApaWiqWJoYbAJopGqkMgvk3BXEfppWOgJIpBpyy0QKZ8JSpGAylAj589BYZBpqa7QWEQbbnP0D29RO5EZgwlWcv-IckkPMv2Zm8La59eS0000

 テスト主導の開発方法に従えば、設計に怪しい兆しが現れる前に、複雑な役割を持ったクラスは分離されるはずだ。なぜならば、複雑な役割をもったクラスのテストを書くのは非常に難しいからだ。しかしそれでもなお、「硬さ」や「もろさ」を感じる場合には FacadeProxy などを使ってリファクタリングするとよいそうだ[要確認]。

余談

 アジャイル開発の奥義を読むまでは、この原則の名前からして、なぜ「クラスの役割は複数存在してはいけない」という内容ではないのかという疑問を持っていた。読んだ結果自分なりには、「変更」という観点に立ったときにはじめて「役割」ごとに分離する必要がでてくるから、「クラスを変更する理由は複数存在してはいけない」と主張しているのではないかと思った。

 たしかに役割は1つのほうが、見通しがよいかもしれないが、複雑にはなる。変更がとうてい生じないような場所に関して、無駄に分離してコードを複雑にする必要がないという意味を込めたい場合、たしかに「クラスを変更する理由は複数存在してはいけない」のほうがよい言葉だと感じた。この解釈自体は、完全に自分が読んだ感想なので、なにか間違っているなどの指摘がある方はコメントいただけるとありがたい。

アジャイル設計の本を読んでいる

 SOLID原則良いなぁ(←適当)…と思いつつ、それが書かれているアジャイルソフトウェア開発の奥義を読んでいなかったので、最近ちらちらと読み始めた。開発プロセスのあたりにはあんまり興味がわかないので、さらさらと軽い感じに読み飛ばしている。開発プロセス関連のはなしはなるほどなぁとは思うし、全員が内容を理解した上でやっていくと意味はありそうなんだけど、現実的にそんなことは起こり得ないと思っていて、学習コストが必要になる開発プロセスに意味はあるのか…とか偉そうなことを思っている。まあそんなことはどうでもよくて、第2部からのアジャイル設計の部分は本当に参考になるなぁ…と思いながら読んでる。

注意:この文章はポエムです

アジャイル設計とは?

 本によると、アジャイルはソフトウェアを少しずつ開発する手法だそうだ。基本的にラビットハウスでは完成させたソフトウェア(完成とは…という話になるが)を納品するようなお仕事ではなく、ソフトウェアを少しずつ拡張していくスタイルなので、まさに合致している。

腐敗するソフトウェアの兆候

 さて、そんな「少しずつ開発していく」スタイルの場合に、どうやってソフトウェアを設計できるのだろうか?という問いが冒頭にくる。少しずつ開発するとしても、最終的な目的は大きなソフトウェアを保守可能な状態で動かすことにあるので、その段階まで持っていけるような状態を各工程(アジャイル言葉ではイテレーションとかよんでるっぽい)で踏んでいかなければならない。それを阻む要素として次のような項目が挙げられている。

  1. 硬さ: 設計変更が困難
  2. もろさ: 設計が壊れやすい
  3. 移植性のなさ: 設計の再利用が困難
  4. 扱いにくさ: 正しい設計をするのが困難
  5. 不必要な複雑さ: 行き過ぎた設計
  6. 不必要な繰り返し: マウスを使いすぎ
  7. 不透明さ: 整然としない表現

 なるほど、たしかにという気持ちになる。こういった構造を作らないための原則として、有名なSOLID原則が紹介されている

  1. 単一責任の原則(SRP: Single Responsibility Principle)
  2. オープンクローズド原則(OCP: Open-Closed Principle)
  3. リスコフの置換原則(LSP: Liskov Substitution Principle)
  4. 依存関係逆転の原則(DIP: Dependency Inversion Principle)
  5. インターフェース分離の原則(ISP: Interface Segregation Principle)

 設計に関するいろいろな本がでているが、基本的にまずこれを守れてない上でそういうものを取り入れようとしても、かえってカオスになるケースのほうが多いのではないかなぁと個人的に感じている。まあ採用しているアーキテクチャなど状況によりけりでしょうけど。とはいえ、ソフトウェアのうまくいっている部分に無理やり、この5原則を適用しにかかるのはやめなさいという忠告も書かれている。そういう部分にはいろいろ変更がくわわってカオスになってきてから、原則に則ってリファクタしても遅くないと思うので、なるほどなぁ…と思った。こいつらについては、別記事で個別にみていく。

硬さ

 ソフトウェアのちょっとした変更が難しい、1行の変更をしようとすると芋づる式に変更箇所が膨らむということを、硬い設計とよんでいる。これって要はその一行に他のモジュールが依存しているのと変わらないので、結局のところモジュールに分割している意味がないってことな気がする。そうならないようにちゃんと考えて書けよ〜ってことなんでしょうね。

もろさ

 1つの変更により他の多くの部分が壊れてしまう傾向のことを、もろい設計というらしい。そういった箇所は、ソフトウェアの部品としての抽象化に失敗しているということを指すのではないかなと思った。

移植性のなさ

 他のシステムでも役に立つ部分を含んでいるのに、その場所から切り離すことが困難・リスクをともなうような状態。これも機能の抽出に失敗してしまっているときに起こるようなことかなと解釈した。移植するかしないかはともかく、移植できる状態にあることは良いことだとも思う。ただ他のシステムでも役に立つが、移植可能な状態にするのがあまりにも厳しいものはあると思うので、そういった部分はまた別の話かなぁとも思う。難しかったり前例がなかったりする実装はそうなってしまいがちなのはしょうがないと思う。バランスが大事。

(ソフトウェアの)扱いにくさ

 設計構造を保持するやり方が難しく、これまでの設計とは違うが圧倒的に簡単に機能を追加したり変更したりできてしまう場合、そのソフトウェアは扱いにくい。これは非常に重要だと思っていて、難しい設計技法とかをあたまのいい人たちが頑張って取り入れても、そこに追っついていける人が開発メンバでなければ、思想のまったくことなる実装が入り組み、余計複雑なソフトウェアを生み出してしまうと思う。同じ設計手法を用いるとしても、その考え方を理解して、やり方を揃えていけるのであればきっとうまくいくのではないかなと思う。そうでない場合は失敗するだろう。当然のことのはずだけど、新しいことを覚えて感動しているときには、こういうバランス感覚を忘れてしまいがちになるはずで、気をつけなければならない。

(環境の)扱いにくさ

 開発環境がクソゴミで非効率なことを指す。コンパイルに時間が掛かる場合に、設計を維持できないとわかっていてもモジュール分割してしまうみたいなこと。差分管理システムへのコミットに時間が掛かるようなことも例として書かれていた。まあgit使ってる分にはよほどなことがない限りこんな感じにはならないかなと思う。モジュール分割については、もうちょっと考えないと本のいっていることの意味が理解しきれない。そもそもコンパイル時間を減らすことを直接の目的としてモジュール分割するのはどうなんだろうか?あたまが悪いので自分には判断がつかない。

不必要な複雑さ

 設計している時点で不必要な要素が含まれている設計のことを指す。これ気をつけないとやってしまいがちですよね。結局あとから手直しするハメにあったり、つかわれることがないまましれっと削除コミットをしたり。このようなことをすると不必要にソフトウェアは複雑になるそうだ。たしかに分かるような、分からないような。不必要なコードをコミットするのは読み手にとっては非常につらみを増やすだけだが、それがソフトウェアの複雑さに結びつくかどうかの部分が自分の中であんまりはっきりとイメージできないので、これからも継続的に考えていきたい。

不必要な繰り返し

 同じようなコードが繰り返しでてくる。読み手がつらい。こういうコードのレビューをしているとき、推敲されていない文章(この記事のように…)を読んでいるかのようでつらい気持ちになる。このリポジトリはお前のブログじゃねえんだよという気持ちにさせないように、ちゃんと自分の書いたコードは推敲しないといけない。でもこれを応用すれば、嫌いなレビュワーを苦しめることができるということか…(嫌いなレビュワーとは…)。そしてこれを放置していると複雑さにつながると思う。

不透明さ

 わかりにくいモジュールのこと。他人が見て、わかりずれえなってところはこれに該当するんじゃないだろうか。モジュールという言葉はJavaのパッケージ的なものだけでなく、普通にサブルーチン、クラスレベルを含んだ話だと解釈した。書いている本人はわかったつもりだけど、誰かが読むと意味不明なのは、日本人の書く英語のようなものなのかなぁと思う。

アジャイルな設計とはなんなのか?

 アジャイルな設計とは、プロセスやイベントではなく、ソフトウェアの構造や可読性を向上させるために、原則・パターン・プラクティスを継続的に適用する行為だそうだ!(意識たかそう!!!)そのための原則とかパターンが8章以降に書かれているので随時まとめていきたい。

モデルビューコントローラー

注: PoEAA, 14-1: Model-View-Controller をまとただけの記事です

UIの相互作用を3つの明確な役割に分割するパターンを「モデルビューコントローラ」とよぶ。

mvc

ViewとControllerは互いに依存してよい。ViewとControllerはそれぞれ、Modelに依存してよい。

パターンの仕組み

 モデルはUIに関わらないドメインについてのデータやふるまいが表現されたオブジェクト。ビューはUIの中でモデルを表現する。ビューは情報の表示だけを責務として負う。コントローラは、ユーザーからの入力を受け取り、モデルを操作して、ビューを適切に更新する。このようにUIはビューとコントローラの協調によって構成される。

プレゼンテーションとモデルの分離

 根本的にプレゼンテーションとモデルは異なる関心事を持っている。ビューはどのように良いUIを提供するかということに興味があり、モデルはどんなロジックでデータを取り扱おうかということに関心がある。したがって分離を行い、各関心事に応じて構造化するために分離を行う。

 非ビジュアルなオブジェクトは、ビジュアルなオブジェクトよりテストがしやすい。当たり前のことではあるが、プレゼンテーションとモデルを分離することにより、ドメインロジックのテストは容易になる。

 プレゼンテーションはモデルに依存するが、モデルはプレゼンテーションに依存しないようにする。こうすることによりモデルを構成するときに、どんなプレゼンテーションから使われるかということを全く意識しなくてすむようになる。また、これによりモデルを利用して新たなプレゼンテーションを追加することが容易にもなる。

複数のウィンドウを持ったクライアントにおける問題

 複数のウィンドウを持つリッチクライアントでは、あるモデルを参照する複数のプレゼンテーションが同時に表示される可能性がある。その際、ユーザーが1つのプレゼンテーションを更新したら、その変更は伝播されなければならない。モデルがプレゼンテーションに依存を作らずにこのようなことを実現するためには、オブザーバーパターンの実装が必要になる。プレゼンテーションはモデルのオブザーバーとして動作させ、モデルが変化するごとにイベントが発生して、情報が更新されるされる仕組みを実現する必要がある。

ビューとコントローラの分離

 こちらは、プレゼンテーションとモデルの分離に比べると重要度は下がる。例えば、編集可能な振る舞いと編集不可能な振る舞いを同時にサポートをしたいときに、ビューとコントローラを分離したいといったモチベーションが生じる。1つのビューに対して、2つのコントローラを作ることにより対応できるが、通常はそのような分離は行われていない。

 プラットフォームによってはUIにおける入力と出力はしばしば不可分であり、GUIフレームワークではビューとコントローラを組み合わせていることが多い。Webインターフェースではコントローラとビューの分離が行われることが多い。

パターンを用いるタイミング

 モデルビューコントローラは、プレゼンテーションとモデルおよび、ビューとコントローラの分離に特徴がある。前者については非常に重要で、ほとんどの場合、分離をすべきである。後者についてはわりあい重要ではなく、必要なときにだけ分離をすることを推奨する。ネイティブクライアントにおいて、分離することはほとんどないが、ウェブフロントエンドにおいて、ビューとコントローラを分離することは珍しくない。

感想

モデルビューコントローラについては POSA を読まないとダメそうだ。正直PoEAAの説明、わりと雑では?と思ってしまった。あと このあたり を読んでおきたい。

Web Presentationのアーキテクチャ

注: PoEAA, Chapter 4: Web Presentation をまとただけの記事です

 ウェブブラウザベースのユーザーインターフェースには、特別なクライアントソフトウェアをインストールしなくてよい、UIに対して共通のアプローチをすることができる、世界中からのアクセスができるなどのメリットがあります。またウェブアプリケーションは、多くの環境でビルドすることが可能であるということは大きな利点です。

 ウェブサーバーアプリケーションは通常、各URLに対してどのプログラムが対応して処理を行うかを記述した設定ファイルを持ちます。これをもとにウェブサーバーはURLを解釈して、それに紐付いたプログラムに制御を渡します。そしてそのプログラムは動的に結果を生成し、レスポンスを返すなどということを行います。このようなウェブサーバーアプリケーションの構造化には、スクリプトとしての構造化とサーバーページとしての構造化という方法があります。

スクリプト形式

 スクリプト形式のプログラムは、HTTP呼び出しに対して対応する関数やメソッドを呼び出して対応するものになります。CGIスクリプトやJavaサーブレットといったものはスクリプト形式に分類されます。スクリプトはさらに細かく分割することができたり、他のサービスを操作することができます。レスポンスはストリームへの書き込みを行うことで実現します。

サーバーページ形式

 スクリプト形式におけるHTTPレスポンスのストリームへの書き込みは非プログラマにとっては困難であり、プログラマであっても煩わしい作業となります。これを解決する方法として、サーバーページ形式というものがあります。これはHTML形式で書かれたレスポンスページ内に実行可能なスクリプトを記述し、ある時点で実行処理を行うというものになります。PHPやASP、JSPなどがサーバーページ形式に分類されます。そもそもASPは、Active Server Pagesの頭文字、JSPは、JavaServer Pagesの頭文字から来ています。スクリプト形式はリクエストの解釈が簡単に行える点で優れています。また、サーバーページ形式はレスポンスのフォーマットを定める方法として優れています。

レイヤ化アーキテクチャ

注: Patterns of Enterprise Application Architecture, Chapter 1 Layering をまとただけの記事です

 レイヤ化は、複雑なソフトウェアのシステムを分割するために使用される技法です。コンピュータの構造やネットワークなどソフトウェア以外の設計でも使われています。各レイヤはその下のレイヤに依存します。レイヤ3はレイヤ2を利用して、レイヤ2はレイヤ1を利用します。レイヤ化アーキテクチャにおいて最も難しいのは、必要なレイヤと各レイヤが行うべき内容を決定することです。

レイヤ化のメリット

  • 他のレイヤを気にせず、1つのレイヤを全体として考えることができる
  • 同じインターフェースを持つだいたい実装でレイヤを置き換えることができる
  • レイヤ間の依存を最小限にできる
  • レイヤは標準化に適している
  • レイヤを構築することにより、多くの高水準サービスがそのレイヤを使用できるようになる

レイヤ化のデメリット

  • 物事をカプセル化してしまうため、連鎖的な変更が起こる場合がある
  • パフォーマンスを損ねる場合がある

 UI上で表示すべきフィールドの追加が、前者の具体的な例になります。追加されるフィールドに対応するものがデータベース上に存在する必要があります。このためUIからデータベースまでのすべてのレイヤにそのフィールドを追加する必要が生じます。後者についてはパフォーマンス損失以上にトランザクションの制御などの最適化によって得られる恩恵が多いケースもあるので検討が必要です。

レイヤ化の発展

 90年代に登場したクライアント/サーバシステムは2レイヤのシステムでした。クライアントはUIとアプリケーションコードを保持して、サーバーは通常のRDBという構成でした。アプリケーションがRDBの内容の表示と更新といった単純なものであれば適切に動作しましたが、妥当性の検証など複雑なドメインロジックを伴い始めると、たちまち重複コードが増えたり、コードの扱いが難しくなったりする局面がでてきました。そのころオブジェクト指向も注目を集めていて、コミュニティではプレゼンテーション、ドメインロジック、データソースの3レイヤにすることにより、ドメインロジックに関する問題を解決しました。最終的にはWebやJavaの登場により、こうしたやり方が受け入れられ、定着することとなりました。

3つのレイヤ

プレゼンテーションレイヤ

 ユーザとソフトウェアの相互作用を扱うのがこのレイヤの役割になります。ユーザーに情報を表示したり、ユーザーからのコマンドをドメインやデータソースでの動作に変換したりするようなコードがここに含まれます。

ドメインレイヤ

 システムそのものであるロジックを置くレイヤです。ビジネスロジックとも呼ばれます。入力データや格納データに基づく計算や、入力データの妥当性検証、また状況に応じたデータソースの選択などがこのレイヤに含まれます。

データソースレイヤ

 他のシステムとの通信を行い、アプリケーションのためにタスクを実行します。

レイヤ化アーキテクチャとヘキサゴナルアーキテクチャ

 ヘキサゴナルアーキテクチャは、外部システムへのインターフェースで囲まれた核としてシステムを視覚化したものです。外部にあるものはすべて外側のインターフェースとします。よって、ヘキサゴナルアーキテクチャは他への提供するサービスと他のサービスを利用することを区別しない、対称的な構造を持っています。対して、レイヤ化アーキテクチャは、他へのサービスとして提供するインターフェースと、他のサービスを利用することを区別する、非対称的なスキーマになっています。そして、この部分がプレゼンテーションレイヤとデータソースレイヤの違いとなっています。

レイヤリングの方法

 システムの規模によって異なります。単純なものであれば、各レイヤのふるまいをサブルーチンとしてあげるだけで大乗ウブです。より複雑なときにはクラスに分割を行い、さらに複雑な場合には幾つかのパッケージにクラスを分割してあげるということができます。状況によって一番適切な分割をするのが望ましいですが、最低でもサブルーチンレベルでの分割を行う必要はありそうです。

レイヤ間の依存関係

 ドメインとデータソースはプレゼンテーションに依存しないようにします。ドメインとデータソースの関係は複雑なため、データソースのアーキテクチャに応じて考えていく必要がありそうです。

レイヤの実行

 PoEAAの内容のかなりの部分は、論理的なレイヤについて解説したものになっています。システムを分割して、その境界部の結合を減らすことについての説明が書かれています。レイヤの分割は1台の物理マシンで実行していたとしても役に立ちます。しかし実行場所が異なることによって違いがもたらされることもあります。

 クライアント/サーバーなアプリケーションを例にとれば、サーバーですべてを実行すると、アプリケーションの更新は容易になります。一方クライアント側での実行はレスポンスや切断時の操作を考えると優れています。