Flyweight (フライウェイト) パターン

インスタンスの生成に時間がかかる小さなオブジェクトを沢山作る場合、共有して効率化を図るデザインパターンです。
1 度作ったインスタンスをどこかに保持しておいて、再度同じインスタンスを生成しないようにする仕組みです。
Flyweight とは、スポーツのボクシングなどの「フライ級」を意味する言葉で、
プログラムの動作を軽くする事を目的としています。


サンプルでは、有名な検索エンジンのロゴの画像ファイルを、HTTP リクエストを投げて取得しています。
画面から画像の表示要求があった場合、都度 Web から画像を取ってくるのは非効率なので、
このパターンの適用により、一度 Web から取得しておいた画像については素早く表示する事が可能です。

このカテゴリーの先頭へ このページの先頭へ

■クラス図

ここで用いるサンプルのクラス図です。
準備中...

このカテゴリーの先頭へ このページの先頭へ

■サンプルの説明

サンプルの見た目1
ComboBox で検索エンジンの名称を選択します。
画像表示ボタンをクリックすると、画面中央に選択した検索エンジンのロゴが表示されます。
サンプルのプロジェクトダウンロード

このカテゴリーの先頭へ このページの先頭へ

■コード

アプリケーションのエントリポイント StartUpForm です。

StartUpForm.vb
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)ストックされている場合は、
わざわざ生成せずに、倉庫にあるストックされたオブジェクトを呼び出し元に返します。

FlyweightImageFactory.vb
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 によって、管理されています。

FlyweightImage.vb
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 から取り出されるモノは、どこで呼んでも同一なわけですから、
どこかの呼び出し元で変更を加えられてしまうようなモノだと、他の箇所に影響を与えてしまうからです。

このカテゴリーの先頭へ このページの先頭へ