連載:実践で役立つ業務アプリ開発のヒント

第3回 クラス構造を整えて再利用性を高める

えムナウ(児玉宏之)
Microsoft MVP Visual Developer - Visual C# JAN 2005 − DEC 2007)
2007/08/28
Page1 Page2

4.コントローラを作成してMVCパターン化

開発ヒント8:MVCパターンの適用と部品化

 MVCパターンとは、プログラムをModel(モデル)、View(ビュー)、Controller(コントローラ)の3要素に分割する開発手法だ。こうすることにより、プログラムの見通しがよくなるとともに、UI部分を別のモジュールに取り換えることが容易になるという利点がある。

 MVCパターンにおいてModelは、データ(例えば品物の価格など)とビジネス・ロジック(例えば品物を買った際の合計額や消費税を計算するなど)を担当する要素である。Model自体はエンド・ユーザーへのプレゼンテーション(=表示や出力)を行わず、データが変更されると、それをViewに通知する。

 Viewはエンド・ユーザーへのプレゼンテーションを担当する要素。Modelからデータの変更が通知されると、そのデータを取り出し、エンド・ユーザーが見るのに適した形に整形して(画面上などに)表示する。

 Controllerは、エンド・ユーザーの入力に応答し、それを処理する要素だ。そこで、何らかの処理が必要な場合にはModelに変更を引き起こし、画面出力などのプレゼンテーションの操作が必要な場合にはViewに変更を引き起こす。

 以上のModel、View、Controllerの関係をまとめたものが次の図だ。

MVCパターンの概念図

 Windowsフォーム開発でこのようなMVCパターンを構成する場合、ModelもControllerもインスタンス自体は、Viewと同じコンピュータに生成するのが便利である。もちろん、システムの規模や負荷の状態によって別のコンピュータに配置してもよい。このように柔軟に配置が選択できるのもMVCパターンの利点である。

 それでは、業務アプリケーションにMVCパターンを適用する方法を考えてみよう。

 MVCパターンでは、前述のとおり、ModelとViewの制御はControllerが担当するので、ModelやViewが持つ個々の(独立・細分化された)機能を制御するための「コマンド」をControllerに追加する。例えば、エンド・ユーザーが詳細画面を操作してデータを保存するという処理を機能ごとに細分化すると、以下のような機能単位が考えられる(これらがコマンドとなり得る)。

(1)入力データのエラー・チェックを行う
(2)本当に保存するかの確認をエンド・ユーザーに提示する
(3)入力データをデータセット(DataSet)にセットする
(4)データセットを実際のデータベースに保存する

 この場合、(1)(2)(3)の機能は(ModelやControllerに何ら影響のない処理なので)Viewの中だけで行って問題ないだろう。しかし(4)の機能はModelの変更を促すことになるので、Controllerがコマンドを通じてModelに変更を引き起こすことになる。

 では具体的な例として、先ほどインターフェイスという形で記述したテーブルアダプタの機能定義(ISearchEditByKeysTableAdapterインターフェイス)に、MVCパターンを適用してみよう。

 MVCパターンで考えると、ISearchEditByKeysTableAdapterインターフェイスは「検索/一覧表示/詳細表示/詳細の追加・変更・削除」という機能を持つModelとなる。そこでここでは、これを制御するControllerを作成することになる。そのControllerも「機能」という側面でとらえると、同様に「検索/一覧表示/詳細表示/詳細の追加・変更・削除」という機能が存在するといえる。機能が存在するなら、インターフェイスで「機能実装の規格」を整理できる。

 そこで今度は、(ISearchEditByKeysTableAdapterインターフェイスというModelを制御する)Controllerに対してインターフェイスを定義してみよう(本稿では、そのインターフェイスの名前を「ISearchEditByKeysController」とした)。

Public Interface ISearchEditByKeysController
  Sub InitializeController(ByVal tableAdapter As ISearchEditByKeysTableAdapter, ByVal bindingSource As BindingSource, ByVal table As DataTable)
  Function Load() As Boolean
  Function LoadByKeys(ByVal keys As DataTableKeys) As Boolean
  Function SearchConditions(ByVal condition As SearchConditions) As Boolean
  Function AddNew() As Object
  Function Save() As Boolean
  Function Delete() As Boolean
End Interface
Controllerに対するインターフェイスの定義(ISearchEditByKeysController.vbファイル)

 「検索(SearchConditions)/一覧表示(Load)/詳細表示(LoadByKeys)/詳細の追加(AddNew)・変更(Save)・削除(Delete)」という機能(=Controllerのコマンド)のほかに、インターフェイスを実装した際に必要になる初期化のための「InitializeControllerメソッド」も一緒に定義しておいた。

 今回は1種類のControllerなので、Modelの制御に必要な機能をまとめて書いたが、ISearchEditByKeysTableAdapterインターフェイスと同じように、細かい機能を別々のインターフェイスに定義して、それらを多重継承によって1つのインターフェイスにまとめることもできる。

 以上で完成したのがControllerのインターフェイスとなる。当然ながら、実際のControllerは、これを実装しなければならない。そこで、このISearchEditByKeysControllerインターフェイスを実装したのが、次のコードである(本稿では、そのControllerの名前を「SearchEditByKeysController」とした)。

Public Class SearchEditByKeysController
  Implements ISearchEditByKeysController

  Protected _dataTable As DataTable
  Protected _bindingSource As BindingSource
  Protected _tableAdapter As ISearchEditByKeysTableAdapter

  Public Overridable Sub InitializeController(ByVal tableAdapter As ISearchEditByKeysTableAdapter, ByVal bindingSource As BindingSource, ByVal table As DataTable) _
      Implements ISearchEditByKeysController.InitializeController
    _dataTable = table
    _bindingSource = bindingSource
    _tableAdapter = tableAdapter
  End Sub

  Public Overridable Function Load() As Boolean Implements ISearchEditByKeysController.Load
    _tableAdapter.FillTable(_dataTable)
    Return True
  End Function

  Public Overridable Function LoadByKeys(ByVal keys As DataTableKeys) As Boolean _
      Implements ISearchEditByKeysController.LoadByKeys
    _tableAdapter.FillTableByKeys(_dataTable, keys)
    Return True
  End Function
  Public Overridable Function SearchConditions(ByVal condition As SearchConditions) As Boolean _
      Implements ISearchEditByKeysController.SearchConditions
    _tableAdapter.FillTableBySearchConditions(_dataTable, condition)
    Return True
  End Function

  Public Overridable Function AddNew() As Object Implements ISearchEditByKeysController.AddNew
    Return _bindingSource.AddNew()
  End Function

  Public Overridable Function Save() As Boolean Implements ISearchEditByKeysController.Save
    _bindingSource.EndEdit()
    _tableAdapter.UpdateTable(_dataTable)
    Return True
  End Function

  Public Overridable Function Delete() As Boolean Implements ISearchEditByKeysController.Delete
    _bindingSource.RemoveCurrent()
    _tableAdapter.UpdateTable(_dataTable)
    Return True
  End Function
End Class
Controller「SearchEditByKeysController」の実装内容(SearchEditByKeysController.vbファイル)

 このController(SearchEditByKeysController)はDataTableクラスとBindingSourceコンポーネント、ISearchEditByKeysTableAdapterインターフェイスに依存する。DataTableクラスやBindingSourceコンポーネントはデータバインディングを使った処理では一般的なものであり、ISearchEditByKeysTableAdapterインターフェイスは「ユーザーDataSet」に固有ではなくなっているので、別のデータセットで同じ機能が必要な場合にも利用できる。つまり、「検索/一覧表示/詳細表示/詳細の追加・変更・削除」の機能を持った処理に対して汎用的に利用できる部品である。

 「ユーザーDataSet」に固有の処理を含めたい場合には、ControllerであるSearchEditByKeysControllerクラスを継承した「ユーザーController」クラスを作成し、固有部分を書くとよい。

 今回はインターフェイスを活用してクラス構造を整理する方法を中心に紹介した。次回はこれをさらに応用した「3段継承」というテクニックをご紹介する。End of Article


 INDEX
  実践で役立つ業務アプリ開発のヒント
  第3回 クラス構造を整えて再利用性を高める
    1.開発ヒント7:インターフェイスで機能を整理
  2.開発ヒント8:MVCパターンの適用と部品化

インデックス・ページヘ  「実践で役立つ業務アプリ開発のヒント」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH