![]() 圖 1:三層圖現(xiàn)在我們有一個(gè)簡(jiǎn)單的邏輯模型。它是如何起作用的?它有助于我們考慮各個(gè)邏輯組之間的邊界。每個(gè)邏輯層應(yīng)盡量與其他層獨(dú)立。理想的情況是,圖層中的更改應(yīng)該對(duì)整體產(chǎn)生最小的影響。例如,如果將數(shù)據(jù)存儲(chǔ)從 SQL Server 更改到 XML 數(shù)據(jù)文件,唯一受到影響的圖層應(yīng)是中間層圖層。用戶界面應(yīng)該根本無(wú)需考慮更改。這會(huì)使您進(jìn)行思考:如何實(shí)現(xiàn)解決方案的實(shí)際編碼以實(shí)現(xiàn)此原則。另外,邏輯層有助于我們考慮安全問(wèn)題。各個(gè)圖層之間的邊界都存在潛在的安全漏洞。而且,各個(gè)圖層可能有自己特定的安全措施(SQL Server 權(quán)限、.NET 運(yùn)行時(shí)權(quán)限、ASP.NET 安全等)。同樣,我們稍后會(huì)在本節(jié)中詳細(xì)討論這個(gè)問(wèn)題。物理體系結(jié)構(gòu)確定邏輯層后,考慮物理層也很重要。例如,您可以在同時(shí)安裝有 SQL Server、Internet Information Server、ASP.NET 和 .NET 運(yùn)行時(shí)的單個(gè)實(shí)際計(jì)算機(jī)上實(shí)現(xiàn)這個(gè)應(yīng)用程序。這將是一個(gè)物理層。但更可靠且可擴(kuò)展的方法是:在由三個(gè) Web 服務(wù)器組成的簇上部署 Web 窗體,在兩個(gè)應(yīng)用服務(wù)器上部署 .NET 組件程序集,在兩個(gè)故障恢復(fù)模式的 SQL Server 上部署數(shù)據(jù)庫(kù)。這樣產(chǎn)生的物理體系結(jié)構(gòu)將七個(gè) Windows 服務(wù)器包含在三個(gè)主要組中:Web 簇、組件簇和數(shù)據(jù)庫(kù)簇。如果您了解系統(tǒng)的不同邏輯部件可以位于不同的計(jì)算機(jī)上,您可能會(huì)實(shí)現(xiàn)不同的代碼。對(duì)于我們的示例,我們采用一個(gè)有效且強(qiáng)大的兩層模型:Web 服務(wù)器托管用戶界面和組件,數(shù)據(jù)庫(kù)服務(wù)器托管 SQL Server 數(shù)據(jù)存儲(chǔ)。如果通信量非常大,這個(gè)模型使我們可以靈活地在簇中添加更多的服務(wù)器,并使其保持足夠的簡(jiǎn)潔以便于處理。下面的圖像顯示了此物理體系結(jié)構(gòu)與前面定義的邏輯體系結(jié)構(gòu)之間的映射關(guān)系。 ![]() 圖 2:物理體系結(jié)構(gòu)與三層體系結(jié)構(gòu)之間的映射關(guān)系正如您看到的那樣,邏輯體系結(jié)構(gòu)和物理體系結(jié)構(gòu)不必相同。在規(guī)劃階段還要考慮一項(xiàng)內(nèi)容:安全。安全規(guī)劃 Microsoft 有一個(gè)關(guān)于安全性與軟件這一主題的歌訣:“Secure by design, secure by default, and secure by deployment(設(shè)計(jì)安全,默認(rèn)安全和部署安全)”。即,在安全中設(shè)計(jì),期待系統(tǒng)在默認(rèn)情況下是安全的,以及創(chuàng)建可以在安全環(huán)境中成功部署的解決方案。安全始終是重要的。既然越來(lái)越多的軟件要在公用的 Internet 上“生存”,編寫(xiě)安全的軟件就更加關(guān)鍵。對(duì)于我們而言,幸運(yùn)的是,.NET 運(yùn)行時(shí)和 Windows 操作系統(tǒng)提供廣泛的安全選項(xiàng)和功能,我們可以輕松地將其包含在我們的應(yīng)用程序中。無(wú)需過(guò)分注重標(biāo)識(shí)和消除聯(lián)機(jī)解決方案中安全漏洞的細(xì)節(jié),我們可以指出其中一些最常見(jiàn)的漏洞并指出我們的應(yīng)用程序規(guī)劃如何進(jìn)行處理。注意:有關(guān)可用選項(xiàng)的詳細(xì)信息,請(qǐng)參閱 Microsoft Security Developer Center。緩沖區(qū)溢出這可能是已編譯應(yīng)用程序中最常見(jiàn)的安全漏洞。由于我們將使用 .NET 運(yùn)行時(shí),而它是設(shè)計(jì)用來(lái)在內(nèi)存中安全運(yùn)行的,因此不太可能發(fā)生緩沖區(qū)溢出。此外,我們使用 Microsoft Visual Basic® .NET 對(duì)解決方案進(jìn)行編碼,而 Microsoft Visual Basic® .NET 不像 C 或 C++ 那樣容易受到緩沖區(qū)溢出問(wèn)題的影響。但是,即使我們打算用 C++ 創(chuàng)建組件,我們還可以使用編譯程序的特殊功能,GS 轉(zhuǎn)換,來(lái)保護(hù)我們免受大多數(shù)緩沖區(qū)溢出的攻擊。數(shù)據(jù)庫(kù)攻擊另一種常見(jiàn)的安全漏洞可能會(huì)使惡意用戶獲得訪問(wèn)存儲(chǔ)在數(shù)據(jù)庫(kù)中的原始數(shù)據(jù)的權(quán)限。為了防止黑客獲得數(shù)據(jù)的控制權(quán),我們僅使用 SQL Server 存儲(chǔ)過(guò)程,而不使用“內(nèi)聯(lián)查詢”。這樣可以大大減少試圖在輸入流中插入其他 SQL 命令的攻擊。我們還在程序中多個(gè)位置處使用輸入驗(yàn)證,以確保所有輸入僅包含有效的字符。交叉站點(diǎn)腳本攻擊對(duì) Web 應(yīng)用程序進(jìn)行的常見(jiàn)攻擊還有一種,它涉及到用戶在輸入流中添加客戶方腳本,這類(lèi)攻擊將執(zhí)行附加的對(duì)話并誘騙用戶將個(gè)人數(shù)據(jù)發(fā)送到黑客自己的 Web 站點(diǎn)。要解決這個(gè)問(wèn)題,我們使用 ASP.NET 1.1 的一個(gè)新功能,過(guò)濾出這種惡意代碼的所有輸入,防止將它置入系統(tǒng)中。顯示屏幕上還包含附加代碼,它將自動(dòng)禁用任何腳本或顯示可能會(huì)插入到數(shù)據(jù)存儲(chǔ)中的標(biāo)記。至此,我們已獲得了應(yīng)用程序的邏輯模型和物理模型,以及確保實(shí)現(xiàn)方案包含的安全功能清單。擁有了這些以及目標(biāo)聲明和用戶方案,我們可以開(kāi)始這次“編碼前”探險(xiǎn)的最后一部分了。完成設(shè)計(jì)文檔 在直接進(jìn)入項(xiàng)目的編碼部分之前,需要花一點(diǎn)時(shí)間實(shí)際勾畫(huà)出應(yīng)用程序的邏輯組件,這非常重要。在我們的示例解決方案中,我們要實(shí)現(xiàn)解決方案的三個(gè)邏輯組件:數(shù)據(jù)庫(kù)、.NET 數(shù)據(jù)訪問(wèn)組件和 ASP.NET 用戶界面。在下面幾篇文章中,我們將非常詳細(xì)地介紹如何實(shí)現(xiàn)這些組件。但現(xiàn)在,我們只是勾畫(huà)出每個(gè)組件的大致輪廓,討論過(guò)程中最重要的方面,即文檔化組件間的交互。數(shù)據(jù)庫(kù)對(duì)于 DotNetKB 應(yīng)用程序,我們需要將數(shù)據(jù)存儲(chǔ)在三張表中:主題、問(wèn)題和回答(請(qǐng)參閱下圖)。 ![]() 圖 3:主題、問(wèn)題和回答表我們需要使用存儲(chǔ)過(guò)程,以使中間層組件也可以安全地訪問(wèn)數(shù)據(jù)。有關(guān)數(shù)據(jù)庫(kù)的細(xì)節(jié),我們將在下一篇文章中討論。這里,我們只是指出:列出表名稱(chēng)及所有列細(xì)節(jié)、默認(rèn)索引和存儲(chǔ)過(guò)程列表的數(shù)據(jù)庫(kù)文檔,應(yīng)該包含在一個(gè)完整的數(shù)據(jù)庫(kù)設(shè)計(jì)文檔中。即,文檔中應(yīng)該具有成功實(shí)現(xiàn)系統(tǒng)數(shù)據(jù)存儲(chǔ)部分所需的詳細(xì)信息。注意:如果留心的話,您可能會(huì)注意到我們未提及將專(zhuān)家數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中。只是為了使項(xiàng)目更加有趣(同時(shí)給我們一個(gè)機(jī)會(huì)使用直接 XML 數(shù)據(jù)存儲(chǔ)),我們將專(zhuān)家信息存儲(chǔ)在一個(gè) XML 數(shù)據(jù)文件中。數(shù)據(jù)訪問(wèn)組件數(shù)據(jù)訪問(wèn)組件設(shè)計(jì)文檔描繪與數(shù)據(jù)存儲(chǔ)系統(tǒng)的交互以及與用戶界面的交互的所有細(xì)節(jié)。在有些系統(tǒng)中,數(shù)據(jù)訪問(wèn)組件實(shí)際上是處理過(guò)程中各種問(wèn)題的多個(gè)程序集。例如,可能會(huì)有一系列業(yè)務(wù)規(guī)則呈現(xiàn)在與數(shù)據(jù)存儲(chǔ)和檢索完全獨(dú)立的用戶界面上。在這種情況下,將業(yè)務(wù)組件與數(shù)據(jù)訪問(wèn)組件分開(kāi)實(shí)現(xiàn)可能比較明智。在我們的示例中,實(shí)際實(shí)現(xiàn)的是兩個(gè)單獨(dú)的組件:Message 組件和 DataAccess 組件。如果在支持基于 XML 的數(shù)據(jù)的傳輸服務(wù)(例如 SOAP Web Service)中進(jìn)行規(guī)劃,這種面向消息的實(shí)現(xiàn)方案將會(huì)特別有成效。消息組件消息組件定義一系列用于在各圖層之間傳輸數(shù)據(jù)的類(lèi)。這些消息可以作為二進(jìn)制或 XML 文本數(shù)據(jù)存在。消息圖層的價(jià)值在于:保護(hù)系統(tǒng)的其余部分,使其獨(dú)立于數(shù)據(jù)存儲(chǔ)實(shí)現(xiàn)方案的具體細(xì)節(jié),例如 SQL Server、XML 文件等。此外,通過(guò)實(shí)現(xiàn)消息圖層而不是更復(fù)雜的“智能對(duì)象”庫(kù),我們的解決方案可以更輕松地支持那些不能同時(shí)發(fā)送數(shù)據(jù)和類(lèi)級(jí)別邏輯的遠(yuǎn)程調(diào)用服務(wù),例如 XML-SOAP。下面是一個(gè)消息類(lèi)示例,在該示例中實(shí)現(xiàn)了 Topic 消息及其集合:Public Class Topic Private _ID As Integer Private _Title As String Private _Description As String Public Property ID() As Integer Get Return _ID End Get Set(ByVal Value As Integer) _ID = Value End Set End Property Public Property Title() As String Get Return _Title End Get Set(ByVal Value As String) _Title = Value End Set End Property Public Property Description() As String Get Return _Description End Get Set(ByVal Value As String) _Description = Value End Set End PropertyEnd ClassPublic Class Topics Inherits System.Collections.CollectionBase Default Public Property Item(ByVal index As Integer) As Topic Get Return CType(List(index), Topic) End Get Set(ByVal Value As Topic) List(index) = Value End Set End Property Public Function Add(ByVal s As Topic) As Integer Return List.Add(s) End Function Public Sub Remove(ByVal index As Integer) List.Remove(index) End SubEnd Class注意:如果您已嘗試過(guò)面向消息的設(shè)計(jì),便會(huì)了解我們想要使這些消息類(lèi)系列化,以便在應(yīng)用程序圖層之間輕松地來(lái)回發(fā)送。幸運(yùn)的是,.NET 運(yùn)行時(shí)知道如何進(jìn)行這項(xiàng)操作,而無(wú)需我們做過(guò)多的工作。但是,當(dāng)我們學(xué)習(xí)創(chuàng)建消息的文章時(shí),我們將詳細(xì)討論 .NET 運(yùn)行時(shí)如何系列化類(lèi),以及我們?nèi)绾芜M(jìn)行操作以使代碼中的過(guò)程最優(yōu)化。在后面實(shí)現(xiàn)消息組件和數(shù)據(jù)訪問(wèn)組件時(shí),文章中將介紹此方法的細(xì)節(jié)。設(shè)計(jì)文檔將包含一個(gè)由所有信息及其屬性與數(shù)據(jù)類(lèi)型組成的列表,F(xiàn)在,我們只是考慮如何使用此消息方法來(lái)封裝圖層間的數(shù)據(jù)傳輸,如何創(chuàng)建一種與本地方案和遠(yuǎn)程方案配合使用的常規(guī)數(shù)據(jù)服務(wù)。數(shù)據(jù)訪問(wèn)組件定義消息類(lèi)的概念后,數(shù)據(jù)訪問(wèn)組件可以集中精力處理與數(shù)據(jù)存儲(chǔ)系統(tǒng)直接對(duì)話的細(xì)節(jié),并以正確的消息格式返回信息。在我們的示例中,這將涉及到使用來(lái)自用戶界面的請(qǐng)求映射 SQL Server 存儲(chǔ)過(guò)程,并創(chuàng)建可返回到用戶界面進(jìn)行顯示的消息(或消息集合)。例如,下面是一個(gè)數(shù)據(jù)訪問(wèn)組件的一部分示例代碼,該組件將從數(shù)據(jù)存儲(chǔ)中檢索單個(gè) Topic 記錄,并將正確的消息格式返回到用戶界面。Public Function GetTopicRecord(ByVal ID As Integer) As Messages.Topic Dim t As Messages.Topic = New Messages.Topic cn = New SqlConnection(secureConnectionString) cd = New SqlCommand("GetTopic", cn) cd.CommandType = CommandType.StoredProcedure cd.Parameters.Add("@ID", ID) cn.Open() dr = cd.ExecuteReader() dr.Read() With t .ID = ID .Title = dr("Title") .Description = dr("Description") End With Return tEnd Function設(shè)計(jì)文檔將包括一系列用于處理來(lái)自用戶界面的各個(gè)請(qǐng)求的類(lèi)和方法,并含有有關(guān)調(diào)用哪個(gè)存儲(chǔ)過(guò)程以及返回何種消息格式的詳細(xì)信息。同樣,我們將在后面主要介紹數(shù)據(jù)訪問(wèn)圖層的文章中討論此過(guò)程的細(xì)節(jié)。Web 用戶界面最后,用戶界面設(shè)計(jì)文檔將包括完成各種方案所需的所有用戶輸入和顯示。通常來(lái)說(shuō),用戶界面文檔包括界面機(jī)制的細(xì)節(jié)以及使用戶界面呈現(xiàn)唯一性的圖形設(shè)計(jì)元素。例如,配色方案、字體和總體頁(yè)面設(shè)計(jì),與用于獲取搜索查詢的正確數(shù)據(jù)的輸入名稱(chēng)和輸入數(shù)量一樣重要。要使文檔簡(jiǎn)潔,通常在一個(gè)與圖形設(shè)計(jì)單獨(dú)的文檔中概要描述機(jī)制細(xì)節(jié)。這是我們將要在示例中做的工作。在后面的一篇文章中,我們將創(chuàng)建一個(gè)綜合性用戶界面文檔和實(shí)現(xiàn)方案,詳細(xì)說(shuō)明每個(gè)屏幕的元素和相關(guān)操作。在另一篇文章中,我們將處理應(yīng)用程序有關(guān)圖形的各個(gè)方面,重點(diǎn)討論作為一種外觀服務(wù)的級(jí)聯(lián)樣式表的使用。下面是一個(gè)典型的用戶界面描述,它涉及“主題”編輯方案。主題輸入屏幕“主題”屏幕將顯示所有當(dāng)前主題(主題 ID 和主題名稱(chēng))的一個(gè)縮略列表,在每個(gè)主題旁邊還將顯示一個(gè)“編輯”鏈接。單擊“編輯”鏈接將會(huì)調(diào)用關(guān)聯(lián)的主題記錄并將其顯示在一系列的輸入框中!皹(biāo)題”和“描述”是可編輯的,而“主題 ID”是只讀的。用戶可以編輯標(biāo)題和描述,然后按“保存”按鈕將更改寫(xiě)入數(shù)據(jù)存儲(chǔ)。輸入將被驗(yàn)證。兩者都是必需的輸入項(xiàng),“標(biāo)題”的長(zhǎng)度限制為 30 個(gè)字符,“描述”的長(zhǎng)度限制為 500 個(gè)字符。更新完成后,將顯示一條響應(yīng)消息指出已確認(rèn)更新;如果更新失敗,則顯示一條消息指出錯(cuò)誤狀況。用戶還可以刪除現(xiàn)有的主題記錄,方法是單擊列表中的“編輯”鏈接,審核顯示屏幕上的記錄細(xì)節(jié)后,單擊“刪除”鏈接。刪除完成后,將顯示一條響應(yīng)消息指出已確認(rèn)更新;如果更新失敗,則顯示一條消息指出錯(cuò)誤狀況。請(qǐng)注意,用戶不能刪除與現(xiàn)有問(wèn)題或回答相關(guān)聯(lián)的主題。此外,用戶可以完整地添加新主題記錄,方法是在初始顯示屏幕上單擊“新建主題”鏈接。將顯示“標(biāo)題”和“描述”輸入(不顯示 ID 輸入)并提供一個(gè)“保存”按鈕。輸入將被驗(yàn)證。兩者都是必需的輸入項(xiàng),“標(biāo)題”的長(zhǎng)度限制為 30 個(gè)字符,“描述”的長(zhǎng)度限制為 500 個(gè)字符。更新完成后,將顯示一條響應(yīng)消息指出已確認(rèn)更新;如果更新失敗,則顯示一條消息指出錯(cuò)誤狀況。利用上面的敘述,您可以輕松地實(shí)現(xiàn)一個(gè)完整的功能屏幕。判斷一個(gè)好的設(shè)計(jì)文檔的方法是:它能夠使讀者完成工作,且不會(huì)提出額外的問(wèn)題。最終的用戶界面設(shè)計(jì)文檔將包括應(yīng)用程序中每個(gè)屏幕的此類(lèi)敘述。小結(jié)并付諸行動(dòng) 我們簡(jiǎn)要介紹了數(shù)據(jù)庫(kù)、中間層和用戶界面實(shí)現(xiàn)方案的最終設(shè)計(jì)文檔。加上體系結(jié)構(gòu)和初始規(guī)劃文檔,它們形成了我們的完整設(shè)計(jì)包。在實(shí)際的情況中,即使是最小的系統(tǒng),完成這些文檔也至少需要幾個(gè)小時(shí)。對(duì)于大型系統(tǒng),可能需要幾周甚至可能幾個(gè)月的時(shí)間。有些人可能會(huì)對(duì)此有一點(diǎn)挫敗感,但是通過(guò)事先完成這些工作,您可以在進(jìn)入項(xiàng)目的編碼階段之前很早就了解完成解決方案面臨的幾乎所有主要障礙。這樣可以減少編寫(xiě)實(shí)際代碼的時(shí)間,并且還可以減少您會(huì)遇到的錯(cuò)誤和障礙的數(shù)量。在下一篇文章中,我們將討論使用 Visual Studio .NET 在 SQL Server 中建立數(shù)據(jù)存儲(chǔ)系統(tǒng)的有關(guān)細(xì)節(jié)。我們將定義數(shù)據(jù)表,創(chuàng)建必需的存儲(chǔ)過(guò)程,并設(shè)置正確的數(shù)據(jù)訪問(wèn),以確保任何組件和數(shù)據(jù)本身之間具有安全可靠的連接。至此,您已經(jīng)看到了一個(gè)如何創(chuàng)建應(yīng)用程序規(guī)劃的可用示例,可以開(kāi)始考慮如何在您自己的工作中使用這些元素來(lái)提高項(xiàng)目的整體質(zhì)量和生產(chǎn)率。有關(guān)項(xiàng)目規(guī)劃以及規(guī)劃如何影響軟件質(zhì)量的詳細(xì)信息,請(qǐng)參閱 Steve McConnell 的 Software Project Survival Guide。Mike Amundsen 提供培訓(xùn)、演講和咨詢服務(wù)。要了解他的詳細(xì)信息或與他聯(lián)絡(luò),請(qǐng)?jiān)L問(wèn)他的站點(diǎn) http://amundsen.com/。此外,還可以在 http://www.amundsen.com/DotNetKB 站點(diǎn)上找到本文的更新和相關(guān)資料。 |
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!