熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

ASP.NET可交互式位圖窗體設計(下)

2013-11-13 09:58:23  來源: .NET編程 

  維護兩個列表

  因為我們要改變對象的填充顏色以實現 Change fill to hot pink 按鈕因此維護了兩個可繪制對象列表一個列表是全部對象另一個列表是可填充對象我們為這兩個列表都使用了 ArrayList 類ArrayList 對象包含一組 Object 引用 這樣一個 ArrayList 可以包含系統中任何類型的混合

  這實際上並沒有什麼幫助 我們希望 ArrayList 僅僅包括可繪制/可填充對象為此我們將 ArrayList 對象設為私有然後將向列表添加對象的過程設為一個方法該方法只接受一個 DShape

  當使用 Add 方法向列表中添加對象時我們將所有對象添加到 wholeList 中然後檢查對象是否還應添加到 filledList 集合中

  請記住Add 方法(以及列表)具有類型安全特性它只接受 DShape(或者從 DShape 派生的類型例如我們在上面創建的所有類型)您不能將整數或字符串添加到列表中這樣我們便可以知道這個列表只包含可繪制對象能夠確知這一點是很方便的!

  繪制項

  我們還有一個 DrawList 方法用於在它作為參數傳遞的 Graphics 對象上繪制列表中的對象此方法具有兩種情況如果列表為空它繪制一個字符串說明列表為空如果列表不為空它使用一個 for each 構造函數遍歷該列表並在每個對象上調用 Draw實際的遍歷和繪圖代碼再簡單不過了

    foreach (DShape d in wholeList)
dDraw(g);
  
  由於列表是封裝的我們知道它具有類型安全特性因此可以僅調用 Draw 方法而不必檢查對象的類型

  最後我們的 Change fills to hot pink(將填充色更改為粉紅)按鈕需要一個對所有可填充對象的引用數組以便更改其 FillBrushColor 屬性雖然可以編寫一個方法遍歷列表並將顏色更改為傳入的值但這一次 Dr GUI 選擇了返回一個對象引用數組幸運的是ArrayList 類具有一個 ToArray 方法利用它可以創建一個傳遞數組該方法獲取我們需要的數組元素類型 從而可以傳遞回所需的類型 IFillable 數組

    public IFillable[] GetFilledList() {
return (IFillable[])filledListToArray(typeof(IFillable));
}
  
  在兩種語言中我們都使用了一個內置運算符獲取給定類型的 Type 對象 在 C# 中是 typeof(IFillable)在 Visual Basic 中是 GetType(IFillable)

  調用程序使用此數組在可填充對象引用數組中遍歷例如將填充顏色更改為粉紅的 Visual Basic 代碼如下所示
  
   Dim filledList As IFillable() = drawingListGetFilledList()
Dim i As IFillable
For Each i In filledList
iFillBrushColor = ColorHotPink
Next  
  用於分解出公共代碼的 Helper 方法和類

  您可能注意到Draw 和 Fill 方法有很多共同的代碼確切地說每個類中創建筆或畫筆的代碼建立 Try/Finally 塊的代碼以及清理筆或畫筆的代碼都是相同的 唯一的區別是進行繪圖或填充時調用的實際方法(由於 C# 中 using 語法非常簡潔因而多余代碼的數量並不明顯)在 Visual Basic NET 中每五行代碼中可能有一行特殊的代碼在所有實現中都是相同的

  總之如果存在大量重復代碼就需要尋求分解出公共的代碼以便形成為所有類所共享的公共子例程這類方法有很多Dr GUI 非常高興為您展示其中的兩種第一種方法僅用於類第二種方法可用於類或接口在本例中只用於接口
  方法 公共入口點調用虛擬方法

  在第一個方法中我們利用了類(不同於接口)可以包含代碼這一事實所以我們提供了一個用於創建筆的 Draw 方法的實現以及一個異常處理程序和 Dispose然後調用實際進行繪圖的 abstract/MustOverride 方法確切地說我們更改了 DShapes 類以適應新的 Draw 方法然後聲明了新的 JustDraw 方法

  

    Public MustInherit Class DShape
Draw 不是虛擬的這似乎有些不尋常……
Draw 本應是抽象的 (MustOverride)
但此方法是繪圖的框架而不是繪圖代碼本身
繪圖代碼在 JustDraw 中完成
還請注意這意味著同原版本相比這些類具有
不同的接口雖然它們完成的工作相同
Public Sub Draw(ByVal g As Graphics)
Dim p = New Pen(penColor)
Try
JustDraw(g p)
Finally
pDispose()
End Try
End Sub
這裡是需要成為多態的部分 因此是抽象的
Protected MustOverride Sub JustDraw(ByVal g As Graphics _
ByVal p As Pen)
Protected bounding As Rectangle
Protected penColor As Color 還應具有屬性
還應具有移動調整大小等方法
End Class


  
  一個值得注意的有趣的地方Draw 方法並不是 virtual/Overridable因為所有派生類都將以相同的方式完成這部分繪圖(如果在 Graphics 上繪圖 [如本例中的定義]則必須指派並清理筆)因此它不需要是 virtual/Overridable

  實際上Dr GUI 認為在本例中Draw 不應該是 virtual/Overridable如果確實要覆蓋 Draw 的行為(而不僅是 JustDraw 的行為)則可以將它設置為 virtual/Overridable但在本例中沒有理由覆蓋 Draw 的行為如果鼓勵程序員進行覆蓋還會帶來隱患 他們可能不會正確處理筆或者使用其他方法繪制對象而不是調用 JustDraw這就違反了我們內置到類中的假設因此將 Draw 設置為非虛擬(順便說一下在 Brand J 中沒有這個選項)可能會降低代碼的靈活性但會更加可靠 Dr GUI 認為在本例中這樣做非常值得

  JustDraw 的典型實現如下所示

    Protected Overrides Sub JustDraw(ByVal g As Graphics ByVal p As Pen)
gDrawEllipse(p bounding)
End Sub
  
  如您所見我們獲得了所希望的簡潔的派生類實現(可填充類中的實現只是略微復雜一些 稍後會看到

  請注意我們在接口中添加了一個額外的公開方法 JustDraw除了要繪制的 Graphics 對象外該方法還引用我們在 Draw 中創建的 Pen 對象因為該方法需要是 abstract/MustOverride因此必須是公開的

  這並不是一個大問題但它確實更改了類的公開接口所以即使這個分解出公共代碼的方法非常簡單方便也應當盡可能選擇其他方法以避免更改公開接口
  方法 虛擬方法調用公共 helper 方法使用回調

  在實現接口的 Fill 方法時代碼的復雜程度也很類似每六行代碼中可能有一行特殊的代碼在所有實現中都是相同的但是我們不能將公共的實現放到接口中因為接口只是聲明它們不包含代碼或數據此外上面列出的方法是不能接受的因為它會更改接口 我們可能並不希望這樣或者因為是其他人創建的接口我們根本不可能更改!

  所以我們需要編寫一個 helper 方法以設置並回調我們的類以便進行實際的填充對於本例Dr GUI 將代碼放在一個單獨的類中這樣任何類都可以使用該代碼(如果采用該方法來實現 Draw則可以將 helper 方法作為抽象基類中的私有方法實現

  暫時不進一步展開以下是我們創建的類
  
    請注意該 delegate 提供的幫助仍然具有多態行為
Class FillHelper
Public Delegate Sub Filler(ByVal g As Graphics ByVal b As Brush)
Shared Sub SafeFill(ByVal i As IFillable ByVal g As Graphics _
ByVal f As Filler)
Dim b = New SolidBrush(iFillBrushColor)
Try
f(g b)
Finally
bdispose()
End Try
End Sub
End Class
  
  我們的 helper 方法調用了 SafeFill該方法接受一個可填充對象(請注意這裡我們使用了 IFillable 接口類型而不是 DShape從而只能傳遞可填充對象)一個要在其上進行繪圖的 Graphics 和一個稱為 delegate 的私有變量我們可以將 delegate 視為一個對方法(而不是對象)的引用 如果您經常使用 C 或 C++ 編程則可以將其視為具有類型安全特性的函數指針可以將 delegate 設置為指向任何具有相同參數類型和返回值的方法無論是實例方法還是 static/Shared 方法將 delegate 設置為指向相應的方法後(例如在調用 SafeFill 時)我們可以通過 delegate 間接調用該方法(順便說一下Brand J 中沒有 delegate這時如果使用此方法會非常困難並且很不靈活)

  delegate 類型 Filler 的聲明位於類聲明之上 它被聲明為一個不返回任何內容(在 Visual Basic NET 中是一個 Sub)並且將 Graphics 和 Brush 作為參數傳遞的方法我們會在將來的專欄中深入討論 delegate

  SafeFill 的操作非常簡單它指派畫筆並將 Try/Finally 和 Dispose 設置為公共代碼它通過調用我們作為參數接收的 delegate 所引用的方法進行各種操作f(g b)

  要使用這個類需要向可填充對象類中添加一個可以通過 delegate 調用的方法並確保將該方法的引用(地址)傳遞到 SafeFill我們將在接口的 Fill 實現中調用 SafeFill以下是 DFilledCircle 的代碼

  

    Public Sub Fill(ByVal g As Graphics) Implements IFillableFill
FillHelperSafeFill(Me g AddressOf JustFill)
End Sub
Private Sub JustFill(ByVal g As Graphics ByVal b As Brush)
gFillEllipse(b bounding)
End Sub


  
  這樣當需要填充對象時便在該對象上調用 IFillableFill它將調用我們的 Fill 方法而 Fill 方法調用 FillHelperSafeFill後者傳遞一個對我們的可填充對象的引用所傳遞的要在其上進行繪圖的 Graphics 對象以及一個對實際完成填充的方法的引用 在本例中該方法是私有的 JustFill 方法

  然後SafeFill 通過 delegate JustFill 方法來設置畫筆和調用JustFill 方法通過調用 GraphicsFillEllipse 進行填充並返回值SafeFill 將清理畫筆並返回到 FillFill 再返回到調用者

  最後是 JustDraw它和原始版本中的 Draw 很類似因為我們都調用了 Fill並調用了基類的 Draw 方法(這是我們以前所做的)以下是相關代碼

    Protected Overrides Sub JustDraw(ByVal g As Graphics ByVal p As Pen)
Fill(g)
MyBaseJustDraw(g p)
End Sub
  
  請記住指派畫筆和筆的復雜之處在於它在 helper 函數中的處理 在 Draw 中它位於基類中在 Fill 中它位於 helper 類中

  如果您認為這比以前復雜了那麼確實如此如果您認為由於額外的調用和需要處理 delegate速度比以前緩慢了也確實如此在生活中總是有很多東西需要進行權衡

  那麼這樣做值得嗎?也許值得這取決於公共代碼的復雜程度以及該代碼需要重復的次數也就是說需要權衡如果我們決定刪除 Try/Finally而只在完成繪圖後清理筆和畫筆代碼便會非常簡單這些方法也就用不上並且在 C# 中using 語句非常簡潔我們也不必費神使用這些方法Dr GUI 認為在 Visual Basic 中使用 Try/Finally 時可以使用也可以不使用這些方法這裡旨在向大家展示這些方法以便在遇到具有大量公共代碼的情況時使用

  維護兩個列表

  因為我們要改變對象的填充顏色以實現 Change fill to hot pink 按鈕因此維護了兩個可繪制對象列表一個列表是全部對象另一個列表是可填充對象我們為這兩個列表都使用了 ArrayList 類ArrayList 對象包含一組 Object 引用 這樣一個 ArrayList 可以包含系統中任何類型的混合

  這實際上並沒有什麼幫助 我們希望 ArrayList 僅僅包括可繪制/可填充對象為此我們將 ArrayList 對象設為私有然後將向列表添加對象的過程設為一個方法該方法只接受一個 DShape

  當使用 Add 方法向列表中添加對象時我們將所有對象添加到 wholeList 中然後檢查對象是否還應添加到 filledList 集合中

  請記住Add 方法(以及列表)具有類型安全特性它只接受 DShape(或者從 DShape 派生的類型例如我們在上面創建的所有類型)您不能將整數或字符串添加到列表中這樣我們便可以知道這個列表只包含可繪制對象能夠確知這一點是很方便的!

  繪制項

  我們還有一個 DrawList 方法用於在它作為參數傳遞的 Graphics 對象上繪制列表中的對象此方法具有兩種情況如果列表為空它繪制一個字符串說明列表為空如果列表不為空它使用一個 for each 構造函數遍歷該列表並在每個對象上調用 Draw實際的遍歷和繪圖代碼再簡單不過了如下面的 Visual Basic 所示

    NET Dim d As DShape
For Each d In wholeList
dDraw(g)
Next
  
  由於列表是封裝的我們知道它具有類型安全特性因此可以僅調用 Draw 方法而不必檢查對象的類型

  返回可填充列表

  最後我們的 Change fills to hot pink(將填充色更改為粉紅)按鈕需要一個對所有可填充對象的引用數組以便更改其 FillBrushColor 屬性雖然可以編寫一個方法遍歷列表並將顏色更改為傳入的值但這一次 Dr GUI 選擇了返回一個對象引用數組幸運的是ArrayList 類具有一個 ToArray 方法利用它可以創建一個傳遞數組該方法獲取我們需要的數組元素類型 從而可以傳遞回所需的類型 IFillable 數組

    NET Public Function GetFilledList() As IFillable()
Return filledListToArray(GetType(IFillable))
End Function


From:http://tw.wingwit.com/Article/program/net/201311/12153.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.