结构图
角色
- 抽象享元(Flyweight)角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数形式传入。
- 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。
- 享元工厂(FlyweightFactory)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个复合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
- 客户端(Client)角色:本角色需要维护一个对所有享元对象的引用。本角色需要自行存储所有享元对象的外蕴状态。
动机
采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价--主要指内存需求方面的代价。
如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
意图
运用共享技术有效地支持大量细粒度的对象。
示意性代码
示意性代码
'MainApp test application
Public Class MainAppClass MainApp
Public Sub Main()Sub Main()
'Arbitrary extrinsic state
Dim extrinsicstate As Integer = 22
Dim f As New FlyweightFactory
'Work with different flyweight instances
Dim fx As Flyweight = f.GetFlyweight("X")
fx.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim fy As Flyweight = f.GetFlyweight("Y")
fy.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim fz As Flyweight = f.GetFlyweight("Z")
fz.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim uf As New UnsharedConcreteFlyweight
uf.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Console.ReadLine()
End Sub
End Class
'"FlyweightFactory"
Public Class FlyweightFactoryClass FlyweightFactory
Private flyweights As New Hashtable
'Constructor
Public Sub New()Sub New()
flyweights.Add("X", New ConcreteFlyweight())
flyweights.Add("Y", New ConcreteFlyweight())
flyweights.Add("Z", New ConcreteFlyweight())
End Sub
Public Function GetFlyweight()Function GetFlyweight(ByVal key As String) As Flyweight
Return TryCast(flyweights(key), Flyweight)
End Function
End Class
'"Flyweight"
Public MustInherit Class FlyweightClass Flyweight
Public MustOverride Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
End Class
'"ConcreteFlyweight"
Public Class ConcreteFlyweightClass ConcreteFlyweight
Inherits Flyweight
Public Overrides Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
Console.WriteLine("ConcreteFlyweight:" & extrinsicstate)
End Sub
End Class
'"UnsharedConcreteFlyweight"
Public Class UnsharedConcreteFlyweightClass UnsharedConcreteFlyweight
Inherits Flyweight
Public Overrides Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
Console.WriteLine("UnsharedConcreteFlyweight:" & extrinsicstate)
End Sub
End Class
'MainApp test application
Public Class MainAppClass MainApp
Public Sub Main()Sub Main()
'Arbitrary extrinsic state
Dim extrinsicstate As Integer = 22
Dim f As New FlyweightFactory
'Work with different flyweight instances
Dim fx As Flyweight = f.GetFlyweight("X")
fx.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim fy As Flyweight = f.GetFlyweight("Y")
fy.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim fz As Flyweight = f.GetFlyweight("Z")
fz.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Dim uf As New UnsharedConcreteFlyweight
uf.Operation(System.Threading.Interlocked.Decrement(extrinsicstate))
Console.ReadLine()
End Sub
End Class
'"FlyweightFactory"
Public Class FlyweightFactoryClass FlyweightFactory
Private flyweights As New Hashtable
'Constructor
Public Sub New()Sub New()
flyweights.Add("X", New ConcreteFlyweight())
flyweights.Add("Y", New ConcreteFlyweight())
flyweights.Add("Z", New ConcreteFlyweight())
End Sub
Public Function GetFlyweight()Function GetFlyweight(ByVal key As String) As Flyweight
Return TryCast(flyweights(key), Flyweight)
End Function
End Class
'"Flyweight"
Public MustInherit Class FlyweightClass Flyweight
Public MustOverride Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
End Class
'"ConcreteFlyweight"
Public Class ConcreteFlyweightClass ConcreteFlyweight
Inherits Flyweight
Public Overrides Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
Console.WriteLine("ConcreteFlyweight:" & extrinsicstate)
End Sub
End Class
'"UnsharedConcreteFlyweight"
Public Class UnsharedConcreteFlyweightClass UnsharedConcreteFlyweight
Inherits Flyweight
Public Overrides Sub Operation()Sub Operation(ByVal extrinsicstate As Integer)
Console.WriteLine("UnsharedConcreteFlyweight:" & extrinsicstate)
End Sub
End Class
一个实例
实例代码
' Flyweight pattern -- Real World example
Imports System
Imports System.Collections
Namespace DoFactoryNamespace DoFactory.GangOfFour.Flyweight.RealWorld
' MainApp test application
Class MainAppClass MainApp
Private Shared Sub Main()Sub Main()
' Build a document with text
Dim document As String = "AAZZBBZB"
Dim chars As Char() = document.ToCharArray()
Dim f As New CharacterFactory()
' extrinsic state
Dim pointSize As Integer = 10
' For each character use a flyweight object
For Each c As Char In chars
pointSize += 1
Dim character As Character = f.GetCharacter(c)
character.Display(pointSize)
Next
' Wait for user
Console.Read()
End Sub
End Class
' "FlyweightFactory"
Class CharacterFactoryClass CharacterFactory
Private characters As New Hashtable()
Public Function GetCharacter()Function GetCharacter(ByVal key As Char) As Character
' Uses "lazy initialization"
Dim character As Character = TryCast(characters(key), Character)
If character Is Nothing Then
Select Case key
Case "A"C
character = New CharacterA()
Exit Select
Case "B"C
character = New CharacterB()
Exit Select
Case "Z"C
'
character = New CharacterZ()
Exit Select
End Select
characters.Add(key, character)
End If
Return character
End Function
End Class
' "Flyweight"
MustInherit Class CharacterClass Character
Protected symbol As Char
Protected width As Integer
Protected height As Integer
Protected ascent As Integer
Protected descent As Integer
Protected pointSize As Integer
Public MustOverride Sub Display()Sub Display(ByVal pointSize As Integer)
End Class
' "ConcreteFlyweight"
Class CharacterAClass CharacterA
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "A"C
Me.height = 100
Me.width = 120
Me.ascent = 70
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
' "ConcreteFlyweight"
Class CharacterBClass CharacterB
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "B"C
Me.height = 100
Me.width = 140
Me.ascent = 72
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
' C, D, E, etc.
' "ConcreteFlyweight"
Class CharacterZClass CharacterZ
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "Z"C
Me.height = 100
Me.width = 100
Me.ascent = 68
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
End Namespace
' Flyweight pattern -- Real World example
Imports System
Imports System.Collections
Namespace DoFactoryNamespace DoFactory.GangOfFour.Flyweight.RealWorld
' MainApp test application
Class MainAppClass MainApp
Private Shared Sub Main()Sub Main()
' Build a document with text
Dim document As String = "AAZZBBZB"
Dim chars As Char() = document.ToCharArray()
Dim f As New CharacterFactory()
' extrinsic state
Dim pointSize As Integer = 10
' For each character use a flyweight object
For Each c As Char In chars
pointSize += 1
Dim character As Character = f.GetCharacter(c)
character.Display(pointSize)
Next
' Wait for user
Console.Read()
End Sub
End Class
' "FlyweightFactory"
Class CharacterFactoryClass CharacterFactory
Private characters As New Hashtable()
Public Function GetCharacter()Function GetCharacter(ByVal key As Char) As Character
' Uses "lazy initialization"
Dim character As Character = TryCast(characters(key), Character)
If character Is Nothing Then
Select Case key
Case "A"C
character = New CharacterA()
Exit Select
Case "B"C
character = New CharacterB()
Exit Select
Case "Z"C
'
character = New CharacterZ()
Exit Select
End Select
characters.Add(key, character)
End If
Return character
End Function
End Class
' "Flyweight"
MustInherit Class CharacterClass Character
Protected symbol As Char
Protected width As Integer
Protected height As Integer
Protected ascent As Integer
Protected descent As Integer
Protected pointSize As Integer
Public MustOverride Sub Display()Sub Display(ByVal pointSize As Integer)
End Class
' "ConcreteFlyweight"
Class CharacterAClass CharacterA
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "A"C
Me.height = 100
Me.width = 120
Me.ascent = 70
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
' "ConcreteFlyweight"
Class CharacterBClass CharacterB
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "B"C
Me.height = 100
Me.width = 140
Me.ascent = 72
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
' C, D, E, etc.
' "ConcreteFlyweight"
Class CharacterZClass CharacterZ
Inherits Character
' Constructor
Public Sub New()Sub New()
Me.symbol = "Z"C
Me.height = 100
Me.width = 100
Me.ascent = 68
Me.descent = 0
End Sub
Public Overloads Overrides Sub Display()Sub Display(ByVal pointSize As Integer)
Me.pointSize = pointSize
Console.WriteLine(Me.symbol + " (pointsize " + Me.pointSize + ")")
End Sub
End Class
End Namespace
Flyweight模式的几个要点:
1、面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
2、Flywight采用对象共享的做法来降低系统中对象的个数,从而降低细粒对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
3、对象的数量太大从而导致对象内存开销加大--什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
参考资料
《C#设计模式(14)-Flyweight Pattern》 吕震宇《C#面向对象设计模式纵横谈系列课程(12)》 李建中老师