Bridge (ブリッジ) パターン
ごちゃごちゃしていて機能拡張するのに困難な状況になっている階層と実装を、大まかな役割毎の階層にわけ、整理して、
それぞれの機能を拡張しやすくするデザインパターンです。
サンプルでは、データベースにアクセスしてファイル作成を行う実装の階層と、ファイルを出力をする機能の階層にわけました。
ファイルを出力する機能の階層のクラスのコンストラクタに、実装の階層のクラスのインスタンスを渡す事により、
Bridge(橋渡し)を実現しています。
Zip 圧縮処理は、vjslib.dll を参照設定して実装しています。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

データを取得する元の DataBase を選択し、Zip 圧縮する場合はチェックボックスにチェックを入れます。
出力ボタンをクリックすると、アプリケーションの開始ディレクトリに、指定された DataBase から取ってきた
jobs テーブルの全レコードのcsvファイルをアプリケーション開始ディレクトリにファイル出力します。
チェックボックスにチェックが入っている場合は、ZIp 圧縮されています。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。
Public Class StartUpForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents Label1 As System.Windows.Forms.Label Friend WithEvents ComboBox1 As System.Windows.Forms.ComboBox Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents CheckBox1 As System.Windows.Forms.CheckBox <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Label1 = New System.Windows.Forms.Label Me.ComboBox1 = New System.Windows.Forms.ComboBox Me.Button1 = New System.Windows.Forms.Button Me.CheckBox1 = New System.Windows.Forms.CheckBox Me.SuspendLayout() ' 'Label1 ' Me.Label1.Location = New System.Drawing.Point(8, 8) Me.Label1.Name = "Label1" Me.Label1.TabIndex = 0 Me.Label1.Text = "Label1" ' 'ComboBox1 ' Me.ComboBox1.Location = New System.Drawing.Point(112, 8) Me.ComboBox1.Name = "ComboBox1" Me.ComboBox1.Size = New System.Drawing.Size(121, 20) Me.ComboBox1.TabIndex = 2 Me.ComboBox1.Text = "ComboBox1" ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(96, 64) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 4 Me.Button1.Text = "Button1" ' 'CheckBox1 ' Me.CheckBox1.Location = New System.Drawing.Point(8, 32) Me.CheckBox1.Name = "CheckBox1" Me.CheckBox1.TabIndex = 5 Me.CheckBox1.Text = "CheckBox1" ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.CheckBox1) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.ComboBox1) Me.Controls.Add(Me.Label1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region 'DataBase Private Const DB_SQLSERVER As String = "SQLServer" Private Const DB_ACCESS As String = "ACCESS" '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Bridge" Me.Label1.Text = "DataBase" Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList Me.ComboBox1.Items.Clear() Me.ComboBox1.Items.Add(Me.DB_SQLSERVER) Me.ComboBox1.Items.Add(Me.DB_ACCESS) Me.ComboBox1.SelectedIndex = 0 Me.CheckBox1.Text = "圧縮する" Me.Button1.Text = "出力" End Sub '出力ボタンをクリックした時のイベント Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim myFileOutputter As FileOutputter Dim outputter As AbstractOutputter 'DataBase If Me.ComboBox1.Text = Me.DB_SQLSERVER Then outputter = New SqlOutputter Else outputter = New OleDbOutputter End If '圧縮の有無 If Me.CheckBox1.Checked Then myFileOutputter = New FileOutputterEx(outputter) Else myFileOutputter = New FileOutputter(outputter) End If '出力 Dim outputpath As String = myFileOutputter.Output("SELECT * FROM jobs") MessageBox.Show("出力が終わりました" + ControlChars.NewLine + outputpath, _ Me.Text, _ MessageBoxButtons.OK, _ MessageBoxIcon.Information) End Sub End Class
実装のクラス階層の抽象の親クラスです。
'実装のクラス階層 Public MustInherit Class AbstractOutputter 'ファイル出力メソッド定義 Public MustOverride Function Output(ByVal sqlScript As String) As String 'DataTableを元にファイル出力を行う Protected Function CsvOutput(ByVal dt As DataTable) As String Dim saveFilePath As String = System.IO.Path.Combine(Application.StartupPath, _ DateTime.Now.ToString("yyyyMMddHHmmss") + ".csv") Dim sw As System.IO.StreamWriter Try sw = New System.IO.StreamWriter(saveFilePath, False, System.Text.Encoding.Default) For Each row As DataRow In dt.Rows For i As Integer = 0 To dt.Columns.Count - 1 If i > 0 Then sw.Write(","c) sw.Write("""" + row(i).toString() + """") Next sw.WriteLine() Next Return saveFilePath Finally If Not sw Is Nothing Then sw.Close() End Try End Function End Class
実装のクラス階層の Access データベースより値を取得してファイル作成を行うクラスです。
AbstractOutputter を継承しています。
'実装のクラス階層 Public Class OleDbOutputter Inherits AbstractOutputter 'ファイル出力メソッド実装 Public Overrides Function Output(ByVal sqlScript As String) As String Dim dt As DataTable Dim mdbPath As String = System.IO.Path.Combine(Application.StartupPath, "Bridge.mdb") Dim connection_String As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _ + mdbPath + ";User Id=admin;Password=;" Dim myDa As CommonDataAccess.AbstructDataAccess myDa = New CommonDataAccess.OleDataAccess Try myDa.Open(connection_String) dt = myDa.DataSelect(sqlScript) Finally If myDa Is Nothing Then myDa.Close() End Try Return MyBase.CsvOutput(dt) End Function End Class
実装のクラス階層の SQLServer データベースより値を取得してファイル作成を行うクラスです。
AbstractOutputter を継承しています。
'実装のクラス階層 Public Class SqlOutputter Inherits AbstractOutputter 'ファイル出力メソッド実装 Public Overrides Function Output(ByVal sqlScript As String) As String Dim dt As DataTable Const CONNECTION_STRING As String = "Data Source=(local);" + _ "Initial Catalog=pubs;" + _ "Integrated Security=SSPI;" Dim myDa As CommonDataAccess.AbstructDataAccess myDa = New CommonDataAccess.SqlServerDataAccess Try myDa.Open(CONNECTION_STRING) dt = myDa.DataSelect(sqlScript) Finally If myDa Is Nothing Then myDa.Close() End Try '出力 Return MyBase.CsvOutput(dt) End Function End Class
機能のクラス階層のクラスです。
Output メソッドは、実装のクラス階層のオブジェクトを用いてファイル作成を行います。
'機能のクラス階層 Public Class FileOutputter '実装の階層のオブジェクト Protected m_outputter As AbstractOutputter 'コンストラクタ(引数のoutputterが橋渡しの役となる) Public Sub New(ByVal outputter As AbstractOutputter) Me.m_outputter = outputter End Sub 'ファイル出力 Public Overridable Function Output(ByVal sqlScript As String) As String Return Me.m_outputter.Output(sqlScript) End Function End Class
機能のクラス階層のクラスです。FileOutputter クラスを拡張し、
Output メソッドで、実装のクラス階層のオブジェクトを用いてファイル作成を行った後、
Zip 圧縮を行います。
'機能のクラス階層 'FileOutputの拡張クラス Public Class FileOutputterEx Inherits FileOutputter 'コンストラクタ Public Sub New(ByVal outputter As AbstractOutputter) MyBase.New(outputter) End Sub '圧縮して出力する Public Overrides Function Output(ByVal sqlScript As String) As String Dim normalOutputPath As String = MyBase.m_outputter.Output(sqlScript) '圧縮 Return Me.ToZipFile(normalOutputPath) End Function 'Zip圧縮 Private Function ToZipFile(ByVal fileName As String) As String Dim zipFileName As String = fileName + ".zip" Dim myZipOutStream As java.util.zip.ZipOutputStream Dim myFileOutputStream As java.io.FileOutputStream myFileOutputStream = New java.io.FileOutputStream(zipFileName) myZipOutStream = New java.util.zip.ZipOutputStream(myFileOutputStream) Dim myFile As java.io.File = New java.io.File(fileName) putFileToZip(myZipOutStream, myFile) '出力ストリームを閉じる myZipOutStream.flush() myZipOutStream.close() Return zipFileName End Function 'ZipOutputStreamへの書き出し Private Sub putFileToZip(ByVal out As java.util.zip.ZipOutputStream, _ ByVal myFile As java.io.File) Dim input As java.io.BufferedInputStream Dim buf(256) As System.SByte Dim crc As java.util.zip.CRC32 = New java.util.zip.CRC32 'CRC32を求める Dim size As Integer input = New java.io.BufferedInputStream(New java.io.FileInputStream(myFile)) While (True) size = input.read(buf, 0, buf.Length) If size = -1 Then Exit While crc.update(buf, 0, size) End While 'エントリの作成 Dim entry As java.util.zip.ZipEntry = New java.util.zip.ZipEntry(myFile.getName()) '圧縮メソッド設定 entry.setMethod(java.util.zip.ZipEntry.DEFLATED) '圧縮前サイズ設定 entry.setSize(myFile.length()) entry.setTime(DateTime.Now.Ticks) 'CRCを設定 entry.setCrc(crc.getValue()) out.putNextEntry(entry) '圧縮元ファイルの内容を書き込む。 input = New java.io.BufferedInputStream(New java.io.FileInputStream(myFile)) Dim sz As Integer While (True) sz = input.read(buf, 0, buf.Length) If sz = -1 Then Exit While crc.update(buf, 0, sz) out.write(buf, 0, sz) End While 'エントリを閉じる out.closeEntry() out.flush() End Sub End Class
■ひとこと
サンプルの場合、後から Zip 圧縮の処理が追加されたのだとしたら、
階層に分けておく事により、機能の追加が容易に出来てしまうわけです。
これが、もし、ファイル出力を行うクラスが、それぞれ SqlServer 用と、OleDb 用に 1 クラスずつしか
存在しなかった場合の事を想像してみて下さい。
また、その上さらに Odbc 用のクラスを追加した場合の事も想像してみて下さい。
実装の階層(SqlOutputter クラス等)と機能を提供する階層(FileOutputter クラス等)を分離しておく事により、
各々の階層の拡張を容易にすることができます。
例えば、機能として『暗号化ファイルを作成する』という機能が追加された場合、
FileOutputter クラスを継承して新たなクラスを 1 つ作り、OutPut メソッドでファイルの暗号化の実装を行えばよいのです。