接下來的技巧會比較複雜一點,不過如果能夠善用的話,就會是一項很好用的武器。我在較大型的專案裡用過這樣的方式,它提供了我在開發程式不一樣的角度。至少我不必再寫一些複雜的判斷式,減少錯誤的發生。當然錯誤還是會有,但這是個人思考邏輯的問題,和物件導向開發方式關係不大。

集合體

設計購物車時,我常用集合體的方式來完成。一個購物車基本型如下:

/inc/class/Shopping/Cart.asp
<%
' 購物車
Class Shopping_Cart
Private List
Private Fields
' 物件初始化
Private Sub Class_Initialize()
Set List = Server.CreateObject("Scripting.Dictionary")
Set Fields = Server.CreateObject("Scripting.Dictionary")
Fields.Add "Amount", 0
End Sub
' 加入商品
Public Sub AddProduct(oProduct)
List.Add List.Count, oProduct
End Sub
' 更新購物車
Public Sub Update()
Dim oProduct
Fields("Amount") = 0
For Each oProduct In List.Items
oProduct.Refresh
Fields("Amount") = Fields("Amount") + oProduct.GetField("SubTotal")
Next
End Sub
' 取得總金額
Public Function GetAmount()
GetAmount = Fields("Amount")
End Function
' 物件結束
Private Sub Class_Terminate()
Set List = Nothing
Set Fields = Nothing
End Sub
End Class
%>

我利用了 Dictionary 物件來幫我維護每項商品物件,也用到了註冊機制。集合體的類型還有很多種,以上是較基本的操作方式。

商品的部份很簡單,就是基本的設定屬性及取得屬性。為了方便說明,我沒有把商品名稱或其他商品屬性放進去。

/inc/class/Shopping/Product.asp
<%
' 商品
Class Shopping_Product
Private Fields
Private Sub Class_Initialize()
Set Fields = Server.CreateObject("Scripting.Dictionary")
Fields.Add "Price", 0
Fields.Add "Quantity", 0
Fields.Add "SubTotal", 0
End Sub
Private Sub Class_Terminate()
Set Fields = Nothing
End Sub
Public Sub SetField(sName, vValue)
If Fields.Exists(sName) Then
Fields(sName) = vValue
End If
End Sub
Public Function GetField(sName)
GetField = Null
If Fields.Exists(sName) Then
GetField = Fields(sName)
End If
End Function
Public Sub Refresh()
Fields("SubTotal") = Fields("Price") * Fields("Quantity")
End Sub
End Class
%>

以下是一個簡單的測試:

<!-- #include virtual="/inc/class/Shopping/Cart.asp" -->
<!-- #include virtual="/inc/class/Shopping/Product.asp" -->
<%
Dim oCart : Set oCart = New Shopping_Cart
Dim oP1 : Set oP1 = New Shopping_Product
Dim oP2 : Set oP2 = New Shopping_Product
Dim oP3 : Set oP3 = New Shopping_Product
oP1.SetField "Price", 100
oP1.SetField "Quantity", 1
oP2.SetField "Price", 120
oP2.SetField "Quantity", 2
oP3.SetField "Price", 150
oP3.SetField "Quantity", 1
oCart.AddProduct oP1
oCart.AddProduct oP2
oCart.AddProduct oP3
oCart.Update
Response.Write oCart.GetAmount
%>

序列化及反序列化

這裡說的序列化其實不是真的序列化,只是我找不到好的名詞來說明。之前提過利用 Class 關鍵字所產生出來的物件是不能存在 Session 中的,因此我會利用 Dictionary 這種用 Server.CreateObject 所產生出來的物件來存放物件資訊。

以上面的購物車為例,如果我想要把商品資料存入 Session ,不能把 Cart 直接存入 Session 中,這樣會使得放在 List 屬性中的商品資訊消失;這時候就要改用 Dictionary 物件來儲存。

商品的序列化動作比較簡單,就是將 Fields 屬性傳回即可;而反序列化時,則是把取出的 Fields 屬性指定回去:

/inc/class/Shopping/Product.asp
<%
' 商品
Class Shopping_Product
Private Fields
' ... 略 ...
Public Function Serialize()
Set Serialize = Fields
End Function
Public Function Unserialize(oFields)
Set Fields = oFields
End Function
End Class
%>

至於購物車的序列化比較複雜一點,首先要將每個商品的序列化存到一個 Dictionary 物件 (oItems),再把這個 Dictionary 物件 (oItems) 放到 Cart 的序列化物件 (oFields) 中,最後回傳 Cart 的序列化物件;反序列化則正好相反。不過反序列化要先產生一個商品物件 (oProduct) ,再把商品的序列化物件 (oItem) 指定給該商品物件。

/inc/class/Shopping/Cart.asp
<%
' 購物車
Class Shopping_Cart
Private List
Private Fields
' ... 略 ....
Public Function Serialize()
Dim oFields : Set oFields = Server.CreateObject("Scripting.Dictionary")
Dim oItems : Set oItems = Server.CreateObject("Scripting.Dictionary")
Dim oProduct
<strong>For Each oProduct In List.Items</strong>
<strong>oItems.Add oItems.Count, oProduct.Serialize</strong>
<strong>Next</strong>
<strong>oFields.Add "Items", oItems</strong>
oFields.Add "Amount", Fields("Amount")
Set Serialize = oFields
Set oItems = Nothing
Set oFields = Nothing
End Function
Public Sub Unserialize(oSerializedFields)
If oSerializedFields Is Nothing Then Exit Sub
<strong>Set List = Server.CreateObject("Scripting.Dictionary")</strong>
Dim oItem
Dim oProduct
For Each oItem In oSerializedFields("Items").Items
<strong>Set oProduct = New Shopping_Product</strong>
<strong>oProduct.Unserialize oItem</strong>
List.Add List.Count, oProduct
Next
Fields("Amount") = oSerializedFields("Amount")
End Sub
End Class
%>

用法如下:

<!-- #include virtual="/inc/class/Shopping/Cart.asp" -->
<!-- #include virtual="/inc/class/Shopping/Product.asp" -->
<%
Dim oCart : Set oCart = New Shopping_Cart
Dim oP1 : Set oP1 = New Shopping_Product
Dim oP2 : Set oP2 = New Shopping_Product
Dim oP3 : Set oP3 = New Shopping_Product
oP1.SetField "Price", 100
oP1.SetField "Quantity", 1
oP2.SetField "Price", 120
oP2.SetField "Quantity", 2
oP3.SetField "Price", 150
oP3.SetField "Quantity", 1
oCart.AddProduct oP1
oCart.AddProduct oP2
oCart.AddProduct oP3
oCart.Update
Set Session("Cart") = oCart.Serialize
Dim oNewCart : Set oNewCart = New Shopping_Cart
oNewCart.Unserialize Session("Cart")
%>

巢狀 Dictionary 物件

這是另一種集合體和序列化的變形,通常是用在把資料庫中的資料轉換成 Dictionary 物件上。主要原理是在迴圈中建立一個 Dictionary 物件 (oItem) ,然後在設定好內容後,把它指定給最外層的 Dictionary 物件 (oItems) 。

<%
Dim oItems : Set oItems = Server.CreateObject("Scripting.Dictionary")
Dim oItem
Dim oField
While Not oTempRS.EOF
Set oItem = Server.CreateObject("Scripting.Dictionary")
' 將所有欄位放到 oItem 中
For Each oField In oTempRS.Fields
oItem.Add oField.Name, oField.Value
Next
oItems.Add oItems.Count oItem
Set oItem = Nothing
Wend
Response.Write oItems(0)("Attr1") &amp; vbCrLf
Response.Write oItems(0)("Attr2") &amp; vbCrLf
Response.Write oItems(1)("Attr1") &amp; vbCrLf
Response.Write oItems(1)("Attr2") &amp; vbCrLf
%>

總結

雖然 ASP (VBScript) 在物件導向上的機制不如 ASP (JScript) 來得先進,但是以上的技巧還是能夠解決多數的問題。這些技巧都是我在開發專案時所得到的心得,如果有更多有趣的方法,也歡迎大家分享。

相關文章