Flyweight (フライウェイト) パターン
インスタンスの生成に時間がかかる小さなオブジェクトを沢山作る場合、共有して効率化を図るデザインパターンです。
1 度作ったインスタンスをどこかに保持しておいて、再度同じインスタンスを生成しないようにする仕組みです。
Flyweight とは、スポーツのボクシングなどの「フライ級」を意味する言葉で、
プログラムの動作を軽くする事を目的としています。
サンプルでは、有名な検索エンジンのロゴの画像ファイルを、HTTP リクエストを投げて取得しています。
画面から画像の表示要求があった場合、都度 Web から画像を取ってくるのは非効率なので、
このパターンの適用により、一度 Web から取得しておいた画像については素早く表示する事が可能です。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

ComboBox で検索エンジンの名称を選択します。
画像表示ボタンをクリックすると、画面中央に選択した検索エンジンのロゴが表示されます。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント 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 ComboBox1 As System.Windows.Forms.ComboBox Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.ComboBox1 = New System.Windows.Forms.ComboBox Me.Button1 = New System.Windows.Forms.Button Me.PictureBox1 = New System.Windows.Forms.PictureBox Me.SuspendLayout() ' 'ComboBox1 ' Me.ComboBox1.Location = New System.Drawing.Point(8, 8) Me.ComboBox1.Name = "ComboBox1" Me.ComboBox1.Size = New System.Drawing.Size(121, 20) Me.ComboBox1.TabIndex = 0 Me.ComboBox1.Text = "ComboBox1" ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(136, 8) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 1 Me.Button1.Text = "Button1" ' 'PictureBox1 ' Me.PictureBox1.Location = New System.Drawing.Point(8, 40) Me.PictureBox1.Name = "PictureBox1" Me.PictureBox1.TabIndex = 2 Me.PictureBox1.TabStop = False ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.PictureBox1) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.ComboBox1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Flyweight" Me.ComboBox1.Items.Clear() Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList Dim ImageDb As DataTable = New DataTable ImageDb.Columns.Add("name", GetType(String)) ImageDb.Columns.Add("url", GetType(String)) ImageDb.Rows.Add(New String() {"google", "http://www.google.co.jp/intl/ja_jp/images/logo.gif"}) ImageDb.Rows.Add(New String() {"goo", "http://www.goo.ne.jp/img/logo/gootop_logo.gif"}) ImageDb.Rows.Add(New String() {"yahoo", "http://i.yimg.jp/images/main13.gif"}) Me.ComboBox1.DataSource = ImageDb Me.ComboBox1.DisplayMember = "name" Me.ComboBox1.ValueMember = "url" Me.ComboBox1.SelectedIndex = 0 Me.Button1.Text = "画像表示" Me.PictureBox1.Size = New Size(270, 210) Me.PictureBox1.Location = New Point(10, 40) Me.PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage Me.PictureBox1.Image = Nothing End Sub '画像表示ボタン押下時のイベント Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim factory As FlyweightImageFactory factory = FlyweightImageFactory.GetInstance() Dim imageUrl As String = DirectCast(Me.ComboBox1.SelectedValue, String) Dim myFlyweightImage As FlyweightImage = factory.GetImage(imageUrl) Me.PictureBox1.Image = myFlyweightImage.GetImage Me.PictureBox1.Refresh() End Sub End Class
画像を生成する工場です。しかし、すでに倉庫(HashTable)ストックされている場合は、
わざわざ生成せずに、倉庫にあるストックされたオブジェクトを呼び出し元に返します。
Imports System.RuntiMe.CompilerServices '画像ファクトリ Public Class FlyweightImageFactory '画像オブジェクトを格納するHashTable Private m_ImageTable As Hashtable = New Hashtable 'Singlleton Private Shared m_flyweightImageFactory As FlyweightImageFactory = New FlyweightImageFactory Private Sub New() End Sub Public Shared Function GetInstance() As FlyweightImageFactory Return m_flyweightImageFactory End Function '画像の取得(排他処理) <MethodImpl(MethodImplOptions.Synchronized)> _ Public Function GetImage(ByVal imageUrl As String) As FlyweightImage If Not Me.m_ImageTable.ContainsKey(imageUrl) Then Me.m_ImageTable.Add(imageUrl, New FlyweightImage(imageUrl)) End If Return DirectCast(Me.m_ImageTable.Item(imageUrl), FlyweightImage) End Function End Class
URL 文字列より画像を取得して保持するクラスです。
FlyweightImageFactory によって、管理されています。
Imports System.Net '指定されたUrlの画像をImageオブジェクトで保持するクラス Public Class FlyweightImage Private m_Image As Image 'Imageアクセサ Public ReadOnly Property GetImage() As Image Get Return Me.m_Image End Get End Property 'コンストラクタ Public Sub New(ByVal imageUrl As String) 'Request Dim req As HttpWebRequest = DirectCast(WebRequest.Create(imageUrl), HttpWebRequest) 'Response Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse) 'Response Stream Dim st As System.IO.Stream = res.GetResponseStream() Try 'StreamからImageを取得 Me.m_Image = Image.FromStream(st) Finally If Not st Is Nothing Then st.Close() End Try res.Close() End Sub End Class
■ひとこと
FlyweightImageFactory は、複数生成する必要がない為、Singleton パターンで実装されています。
Flyweight パターンを適用する事により、先に述べた通り、重複したインスタンスを生成する事がなくなる他、
インスタンスの管理を Factory に任せてやれば、既に生成したか・してないかの管理を
実際にインスタンスを使う側ではやる必要がなくなります。
ただし、Flyweight パターンで、Factory が管理するようなモノは、状態遷移がないものに限ります。
Factory から取り出されるモノは、どこで呼んでも同一なわけですから、
どこかの呼び出し元で変更を加えられてしまうようなモノだと、他の箇所に影響を与えてしまうからです。