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

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

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

 前々回は、データベース処理関連の実践的な開発手法について説明した。また前回はユーザー・インターフェイス(UI)回りを効率的に開発する手法を説明した。

 今回は、クラス構造を整えて再利用性を高める手法を紹介する。なお、使用するデータについては前回までと同じものである。

3. 機能を整理して共通化する

 業務アプリケーションを開発していると、データセット(DataSet)やテーブルアダプタ(TableAdapter)に対する処理を書く際に、「同じようなコードをほかにも書いたのでは?」と感じることがあるだろう。実際に、ユーザー管理や取引先管理など、データベースのテーブルが変わるだけで、それ以外は同じ処理を行うということはよくあることである。

 そういうときは、データベースのテーブルが変わっても共通で使える部分を抽出してまとめ、再利用可能な部品を作ると便利だ。そのためには、機能を整理して考えてみることが大切である。機能を整理する手段の1つにインターフェイス(Interface)がある。

開発ヒント7:インターフェイスで機能を整理

 インターフェイスとはクラスが実装しなければならないプロパティやメソッドやイベントの定義のみを列挙した抽象型クラスのことであるが、複数種類のオブジェクトに共通する機能を実装するための規格を定義する目的によく利用される。

 それでは実際に、前々回作成したテーブルアダプタのメソッドに対するインターフェイスを定義してみよう。具体的には次のようなコード(ITableAdapters.vbファイル)になる。

Public Interface IFillTable
  Sub FillTable(ByVal table As DataTable)
End Interface

Public Interface IUpdataTable
  Sub UpdateTable(ByVal table As DataTable)
End Interface

Public Interface IFillTableByKeys
  Sub FillTableByKeys(ByVal table As DataTable, ByVal keys As DataTableKeys)
  Function GetKeys(ByVal row As DataRow) As DataTableKeys
End Interface

Public Interface IFillTableBySearchConditions
  Sub FillTableBySearchConditions(ByVal table As DataTable, ByVal condition As SearchConditions)
End Interface

Public Interface IFillTableAdapter
    Inherits IFillTable
End Interface

Public Interface ISearchEditByKeysTableAdapter
    Inherits IFillTable, IUpdataTable, IFillTableByKeys, IFillTableBySearchConditions
End Interface
テーブルアダプタのメソッドに対するインターフェイスの定義(ITableAdapters.vbファイル)
インターフェイス(Interface)の名前は、大文字の「I」を先頭に付けた名前にするのが一般的である。

 今回は、テーブル全体の読み込みしか行わない「都道府県TableAdapter」のための「IFillTableAdapter」インターフェイスと、「検索/一覧表示/詳細表示/詳細の追加・変更・削除」の機能を持った「ユーザーTableAdapter」のための「ISearchEditByKeysTableAdapter」インターフェイスを定義した。

 これらは、細かい機能を複数のインターフェイス(IFillTableやIUpdataTable、IFillTableByKeysなど)に定義して、それを多重継承することによって、IFillTableAdapterやISearchEditByKeysTableAdapterという1つのインターフェイスに機能をまとめている。

 このようにインターフェイスを活用することで、「機能実装の規格」を上手に整理できるようになる。こうしておけば、複数のオブジェクトに共通の機能をすべて同様の規格で実装できるため、管理性や仕様の統一性が高まる。開発が進んでシステム規模が大きくなるほど、その効果が得られるだろう。

 また、汎用クラスでインターフェイスを利用すれば、複数の汎用クラスなどで共通する機能の実装規格を、インターフェイスにまとめて整理することもできるので、さらに一層、機能の整理が進む。その例を示そう。

 前々回

「ユーザーDataSet」に固有な「ユーザーDataTable」や「ユーザーRow」などのクラスは、「DataTable」や「DataRow」といった汎用的なクラスにラップすると便利だ

と書いたが、汎用クラスにすることによって共通した機能定義を書くことができる。この機能定義は、「ユーザーDataSet」に固有ではなく(=抽象化・汎用化されている)、別のデータセット(DataSet)で同じ機能が必要な場合にも利用できる。このような機能定義の実装規格を定めるためにインターフェイスを利用してみよう。

 ここでは、先ほど作成したインターフェイスを実装する形で、前々回作成したテーブルアダプタのメソッドを書き直してみる。実際に書き直したのが次のコードだ。

Option Strict On

Partial Class 都道府県DataSet
End Class

Namespace 都道府県DataSetTableAdapters
  Partial Public Class 都道府県TableAdapter
    Implements IFillTableAdapter

    Public Sub FillTable(ByVal table As DataTable) Implements IFillTableAdapter.FillTable
      Me.Fill(CType(table, 都道府県DataSet.都道府県DataTable))
    End Sub

  End Class
End Namespace
テーブルアダプタ「都道府県DataSetTableAdapters」の実装内容(都道府県DataSet.vbファイル)

Option Strict On

Partial Class ユーザーDataSet

  Public Class ユーザーDataTableKeys
    Inherits DataTableKeys

    Public ID As Integer
    Public Sub New(ByVal ID As Integer)
      Me.ID = ID
    End Sub

  End Class

  Public Class ユーザーSearchConditions
    Inherits SearchConditions

    PublicAs String
    PublicAs String
    Public ユーザー名 As String
    Public Sub New(ByValAs String, _
                   ByValAs String, _
                   ByVal ユーザー名 As String)
    Me.姓 = 姓
    Me.名 = 名
    Me.ユーザー名 = ユーザー名
  End Sub

  End Class
End Class

Namespace ユーザーDataSetTableAdapters
  Partial Public Class ユーザーTableAdapter
    Implements ISearchEditByKeysTableAdapter

    Public Sub FillTable(ByVal table As DataTable) _
        Implements ISearchEditByKeysTableAdapter.FillTable
      Me.Fill(CType(table, ユーザーDataSet.ユーザーDataTable))
    End Sub

    Public Sub UpdateTable(ByVal table As DataTable) _
        Implements ISearchEditByKeysTableAdapter.UpdateTable
      Me.Update(CType(table, ユーザーDataSet.ユーザーDataTable))
    End Sub

    Public Sub FillTableByKeys(ByVal table As DataTable, ByVal keys As DataTableKeys) _
        Implements ISearchEditByKeysTableAdapter.FillTableByKeys
      Me.FillByKeys(CType(table, ユーザーDataSet.ユーザーDataTable), CType(keys, ユーザーDataSet.ユーザーDataTableKeys).ID)
    End Sub

    Public Function GetKeys(ByVal row As DataRow) As DataTableKeys _
        Implements ISearchEditByKeysTableAdapter.GetKeys
      Return New ユーザーDataSet.ユーザーDataTableKeys(CType(row, ユーザーDataSet.ユーザーRow).ID)
    End Function

    Public Sub FillTableBySearchConditions(ByVal table As DataTable, ByVal condition As SearchConditions) _
        Implements ISearchEditByKeysTableAdapter.FillTableBySearchConditions
      Dim 検索条件 As ユーザーDataSet.ユーザーSearchConditions = CType(condition, ユーザーDataSet.ユーザーSearchConditions)
      Me.FillBySearchConditions(CType(table, ユーザーDataSet.ユーザーDataTable), 検索条件.姓, 検索条件.名, 検索条件.ユーザー名)
    End Sub

  End Class
End Namespace
テーブルアダプタ「ユーザーDataSetTableAdapters」の実装内容(ユーザーDataSet.vbファイル)

 このように、汎用クラスにインターフェイスを組み合わせることで、その機能実装も、うまく整理できる。

 ユーザーTableAdapterはユーザーDataSetを対象にしたメソッドを提供するクラスであるが、ISearchEditByKeysTableAdapterインターフェイスにキャストして利用することにより別のデータセット(DataSet)で同じ機能が必要な場合にも利用する側のプログラムを変更する必要がないため、再利用性が高まる。例えば以下で説明するコントローラはISearchEditByKeysTableAdapterインターフェイスを対象にしているので、「検索/一覧表示/詳細表示/詳細の追加・変更・削除」という機能を持ちISearchEditByKeysTableAdapterインターフェイスを継承しているテーブルアダプタ(TableAdapter)すべてに対応できる。


 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