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

第4回 適切な粒度でライブラリ化して開発資産にする

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

 第1回は、データベース処理関連の実践的な開発手法について説明した。また第2回はユーザー・インターフェイス回りを効率的に開発する手法を説明した。第3回は、クラス構造を整えて再利用性を高める手法を説明した。

 今回は、実装を細分化して適切な粒度でまとめ、それをライブラリ化することで開発資産にする手法を紹介する。今回が本連載の最終回となる。なお、使用するデータについては前回までと同じものである。

5. 三段継承

開発ヒント9:三段継承

 三段継承とは、ユーザー・インターフェイス開発を分割して小さくすることで、効率的に開発する手法である。名前のとおり、その分割単位の粒度を3段で構成する。1段目の部品を2段目が継承し、2段目の部品を3段目が継承するという仕組みになる。その部品の作成指針は以下のとおりだ(題材として第2回で作成したサンプルを活用する)。

 まず1段目。第2回に説明したように、各コントロールに共通する処理を1つのコントロール(やフォーム)にラップして部品化する。いわゆる「ラッパー・コントロール(もしくはラッパー・フォーム)」(共通処理単位)を作成する。

 2段目。Sample1SearchControlコントロール(検索画面パーツ)/Sample1ListControlコントロール(一覧画面パーツ)/Sample1DetailControlコントロール(詳細画面パーツ)の基本機能部分のみを実装したSearchBaseControlコントロール(検索機能)/ListBaseControlコントロール(一覧機能)/DetailBaseControlコントロール(詳細機能)などの「機能コントロール」(共通機能単位)を作成する。このとき1段目のラッパー・コントロールを継承する。

 そして3段目。それらの機能コントロールを継承してSample2SearchControl(検索画面パーツ)/Sample2ListControl(一覧画面パーツ)/Sample2DetailControl(詳細画面パーツ)などの「ユーザー・コントロール」(画面パーツ単位)を作成する。これらのコントロールは、最初のSample1SearchControlコントロール(検索画面パーツ)/Sample1ListControlコントロール(一覧画面パーツ)/Sample1DetailControlコントロール(詳細画面パーツ)から機能コントロールの内容を差し引いたものだ。

 要するに、Sample1SearchControlコントロール(検索画面パーツ)/Sample1ListControlコントロール(一覧画面パーツ)/Sample1DetailControlコントロール(詳細画面パーツ)を機能部分とそれ以外に分離して、機能部分を2段目に、それ以外を3段目に配置するわけだ。このように分離しておけば、3段目で機能を実装する必要がなくなり、共通の機能を繰り返し記述する必要がなくなる。一般的ではない特殊な機能があれば3段目で実装すればよい。

 1つずつコードを見ていこう。

 1段目ではユーザー・コントロール(UserControlクラス)を継承するラッパー・コントロールとして「BaseUserControl」クラス(BaseUserControl.vbファイル)を作成する。ここでは、何も共通する処理がないので、コードは追加しない。

Public Class BaseUserControl
End Class
BaseUserControlクラスのコード内容(BaseUserControl.vbファイル)
ユーザー・コントロールは[新しい項目の追加]ダイアログで、「ユーザー コントロール」テンプレートを選択して作成する。ここに示しているコードでは分からないが、その際に生成されるBaseUserControl.Desingner.vbコード・ビハインド・ファイルに、「Inherits System.Windows.Forms.UserControl」という記述があるため、BaseUserControlクラスはユーザー・コントロールを継承している。

 2段目では、BaseUserControlクラスを継承するユーザー・コントロールとして、

  • SearchBaseControlコントロール(検索機能)
  • ListBaseControlコントロール(一覧機能)
  • DetailBaseControlコントロール(詳細機能)

を作成する。

 まずDetailBaseControlコントロールとして、「DetailBaseControl」クラス(DetailBaseControl.vbファイル)を作成する。具体的には以下のようなコードになる。ここでは「ISearchEditByKeysController」「ISearchEditByKeysTableAdapter」などのインターフェイス(Interface)を使っているので、(SearchBaseControl/ListBaseControl/DetailBaseControlコントロール間で)実装の規格が汎用化されていることが分かるだろう。

Public Class DetailBaseControl

  Private _dataTable As DataTable
  Private _bindingSource As BindingSource
  Private _tableAdapter As ISearchEditByKeysTableAdapter
  Private _controller As ISearchEditByKeysController

  Public Overridable Sub InitializeControl( _
      ByVal controller As ISearchEditByKeysController, _
      ByVal dataTable As DataTable, _
      ByVal bindingSource As BindingSource, _
      ByVal tableAdapter As ISearchEditByKeysTableAdapter)
    _controller = controller
    _dataTable = dataTable
    _bindingSource = bindingSource
    _tableAdapter = tableAdapter
  End Sub

  Public Overridable Function AddNewData() As Object
    Return _controller.AddNew()
  End Function

  Public Overridable Function LoadData(ByVal keys As DataTableKeys) As Boolean
    If keys Is Nothing Then Return AddNewData()
    Return _controller.LoadByKeys(keys)
  End Function

  Public Overridable Function SaveData() As Boolean
    If Not Me.ValidateChildren() Then Return False
    Return _controller.Save()
  End Function

  Public Overridable Function DeleteData() As Boolean
    Dim result As DialogResult = MessageBox.Show(My.Resources.QuestionDelete, Me.ParentForm.Text, MessageBoxButtons.OKCancel)
    If result <> DialogResult.OK Then Return False
    Return _controller.Delete()
  End Function

End Class
DetailBaseControlクラスのコード内容(DetailBaseControl.vbファイル)
BaseUserControlクラスを継承するユーザー・コントロールを作成するには、[新しい項目の追加]ダイアログで、「継承されたユーザー コントロール」というテンプレートを選び、ファイルが生成される前に表示される[継承ピッカー]ダイアログで「BaseUserControl」を選択すればよい。ここに示しているコードでは分からないが、その際に生成されるBaseUserControl.Desingner.vbコード・ビハインド・ファイルに、「Inherits BaseUserControl」という記述があるため、BaseUserControlクラスはユーザー・コントロールを継承している。

 なお上記コードのSaveDataメソッドでは、基底クラスUserControlのValidateChildrenメソッドを呼んでいるので、コントロールのValidatingイベント・ハンドラで入力エラー・チェックなどの検証処理が行える。ただし、これについては本稿の目的外なので今回は実装していない。

 続けて、ListBaseControlコントロールとSearchBaseControlコントロールを実装する。コード内容は、次のようになる。

Public Class ListBaseControl

  Private _dataGridView As DataGridView
  Private _dataTable As DataTable
  Private _bindingSource As BindingSource
  Private _tableAdapter As ISearchEditByKeysTableAdapter
 Private _controller As ISearchEditByKeysController

  Public Overridable Sub InitializeControl( _
      ByVal controller As ISearchEditByKeysController, _
      ByVal dataGridView As DataGridView, _
      ByVal dataTable As DataTable, _
      ByVal bindingSource As BindingSource, _
      ByVal tableAdapter As ISearchEditByKeysTableAdapter)
    _controller = controller
    _dataGridView = dataGridView
    _dataTable = dataTable
    _bindingSource = bindingSource
    _tableAdapter = tableAdapter
    RebindBindingSource()
  End Sub

  Public Overridable Sub RebindBindingSource()
    _dataGridView.DataSource = _bindingSource
  End Sub

  Public Overridable Function FillTable() As Boolean
    Return _controller.Load()
  End Function

  Public Overridable Function GetCurrentKeys() As DataTableKeys
    Dim row As DataRow = DataGridViewHelper.GetCurrentRow(_dataGridView)
    If row Is Nothing Then Return Nothing
    Return _tableAdapter.GetKeys(row)
  End Function

  Public Overridable Function SearchTable(ByVal condition As SearchConditions) As Boolean
    Return _controller.SearchConditions(condition)
  End Function

End Class
ListBaseControlクラスのコード内容(ListBaseControl.vbファイル)

Public Class SearchBaseControl

  Public Overridable Sub ClearSearchConditions()
  End Sub

  Public Overridable Function GetSearchConditions() As SearchConditions
    Return Nothing
  End Function

End Class
SearchBaseControlクラスのコード内容(SearchBaseControl.vbファイル)

 SearchBaseControlコントロール(検索機能)に関しては画面の項目に依存しているので、2段目ではメソッドの枠だけ作っておくことにする。

 3段目であるSample2ListControl(一覧画面パーツ)/Sample2DetailControl(詳細画面パーツ)は、機能がなくコードもない。

Public Class Sample2DetailControl
End Class
Sample2DetailControlクラスのコード内容(Sample2DetailControl.vbファイル)
Sample2DetailControlクラスはDetailBaseControlクラスを継承するユーザー・コントロールである。

Public Class Sample2ListControl
End Class
Sample2ListControlクラスのコード内容(Sample2ListControl.vbファイル)
Sample2ListControlクラスはListBaseControlクラスを継承するユーザー・コントロールである。

 Sample1SearchControlコントロール(検索画面パーツ)は、2段目のSearchBaseControlコントロールを継承するユーザー・コントロールだが、そのSearchBaseControlコントロールではメソッドの枠しか実装しなかったので、ここで実際のコードを記述する。次のようなコードになる。

Public Class Sample2SearchControl

  Public Overrides Sub ClearSearchConditions()
    Me.姓BaseTextBox.Text = ""
    Me.名BaseTextBox.Text = ""
    Me.ユーザー名BaseTextBox.Text = ""
  End Sub

  Public Overrides Function GetSearchConditions() As SearchConditions
    Return New ユーザーDataSet.ユーザーSearchConditions( _
        Me.姓BaseTextBox.Text, _
        Me.名BaseTextBox.Text, _
        Me.ユーザー名BaseTextBox.Text)
  End Function

End Class
Sample2SearchControlクラスのコード内容(Sample2SearchControl.vbファイル)
Sample2SearchControlクラスはSearchBaseControlクラスを継承するユーザー・コントロールである。

 もちろん3段目には、実際にはSample1SearchControlコントロール(検索画面パーツ)と同じ、コントロールや「ユーザーDataSet」「ユーザーBindingSource」「ユーザーTableAdapter」といったものは配置する必要があるが、機能的には2段目で実装しているので継承側で実装する必要はない。

3段目のSample2DetailControlユーザー・コントロールに配置するコントロールの例(Sample2DetailControl.vbファイル)

 最後にWindowsフォームとして「Sample2Form」クラス(Sample2Form.vbファイル)を作成し、そのフォーム上にSample2SearchControl(検索画面パーツ)/Sample2ListControl(一覧画面パーツ)/Sample2DetailControl(詳細画面パーツ)を配置する。Sample2Formクラスのコード内容は以下のようになる。

Public Class Sample2Form

  Private controller As New ユーザーController

  Private Sub Sample2Form_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    RebindBindingSource()
    InitializeControls()
    Me.ユーザーTableAdapter.Fill(Me.ユーザーDataSet.ユーザー)
  End Sub

  Private Sub RebindBindingSource()
    Me.ユーザーDataSet = Me.Sample2DetailControl1.ユーザーDataSet
    Me.ユーザーTableAdapter = Me.Sample2DetailControl1.ユーザーTableAdapter
    Me.ユーザーBindingSource = Me.Sample2DetailControl1.ユーザーBindingSource
    Me.ユーザーBindingNavigator.BindingSource = Me.ユーザーBindingSource
    Me.Sample2ListControl1.ユーザーDataSet = Me.ユーザーDataSet
    Me.Sample2ListControl1.ユーザーTableAdapter = Me.ユーザーTableAdapter
    Me.Sample2ListControl1.ユーザーBindingSource = Me.ユーザーBindingSource
    Me.Sample2ListControl1.ユーザーBaseDataGridView.DataSource = Me.ユーザーBindingSource
  End Sub

  Private Sub InitializeControls()
    Me.Sample2DetailControl1.InitializeControl(controller, Me.ユーザーDataSet.ユーザー, Me.ユーザーBindingSource, Me.ユーザーTableAdapter)
    Me.Sample2ListControl1.InitializeControl(controller, Me.Sample2ListControl1.ユーザーBaseDataGridView, Me.ユーザーDataSet.ユーザー, Me.ユーザーBindingSource, Me.ユーザーTableAdapter)
    Me.Sample2SearchControl1.ClearSearchConditions()
    controller.InitializeController(Me.ユーザーTableAdapter, Me.ユーザーBindingSource, Me.ユーザーDataSet.ユーザー)
  End Sub

  Private Sub BindingNavigatorAddNewItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BindingNavigatorAddNewItem.Click
    Me.Sample2DetailControl1.AddNewData()
  End Sub

  Private Sub BindingNavigatorDeleteItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BindingNavigatorDeleteItem.Click
    Me.Sample2DetailControl1.DeleteData()
  End Sub

  Private Sub ユーザーBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ユーザーBindingNavigatorSaveItem.Click
    Me.Sample2DetailControl1.SaveData()
  End Sub

  Private Sub SearchToolStripButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SearchToolStripButton.Click
    Me.Sample2ListControl1.SearchTable(Me.Sample2SearchControl1.GetSearchConditions())
  End Sub

End Class
Sample2Formクラスのコード内容(Sample2Form.vbファイル)

 以上で完成した三段継承を実際に実装したときのクラス/オブジェクト構造が以下の画面である。

三段継承を実際に実装したときのクラス/オブジェクト構造

 以下の表はこの図をまとめ直したものである。「画面パーツ単位」とは本文の説明の「画面パーツ」を表し、「共通機能単位」は「機能コントロール」を、「共通処理単位」は「ラッパー・コントロール(ラッパー・フォーム)」を意味している。

ユーザー・インターフェイス 3段目
(画面パーツ単位)
2段目
(共通機能単位)
1段目
(共通処理単位)
検索画面 Sample2SearchControl SearchBaseControl BaseUserControl
詳細画面 Sample2DetailControl DetailBaseControl
一覧画面 Sample2SearchControl SearchBaseControl
三段継承を実際に実装したときのクラス/オブジェクト構造のまとめ

 2段目の機能コントロールがポイントである。3段目の内容が、たとえユーザー管理と取引先管理のように取り扱うデータの種類が全然違っていたとしても、そこに共通した機能があれば(例えばデータの追加/更新/削除などの機能は共通化しやすい)、それを2段目で汎用化して使い回せるようにしたい。2段目でデータバインディング部分(データセット/テーブルアダプタ/BindingSourceコンポーネント)を取り扱うには、「ユーザーDataSet/ユーザーTableAdapter/ユーザーBindingSource」ではなく、「DataSet/TableAdapter/BindingSource」など基本型を利用する必要がある。


 INDEX
  実践で役立つ業務アプリ開発のヒント
  第4回 適切な粒度でライブラリ化して開発資産にする
  1.開発ヒント9:三段継承
    2.開発ヒント10:フォルダとプロジェクトで整理

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


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