<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      《解剖PetShop》系列之二

      二、PetShop數據訪問層之數據庫訪問設計
      在系列一中,我從整體上分析了PetShop的架構設計,并提及了分層的概念。從本部分開始,我將依次對各層進行代碼級的分析,以求獲得更加細致而深入的理解。在PetShop 4.0中,由于引入了ASP.Net 2.0的一些新特色,所以數據層的內容也更加的廣泛和復雜,包括:數據庫訪問、Messaging、MemberShip、Profile四部分。在系列二中,我將介紹有關數據庫訪問的設計。

      在PetShop中,系統需要處理的數據庫對象分為兩類:一是數據實體,對應數據庫中相應的數據表。它們沒有行為,僅用于表現對象的數據。這些實體類都被放到Model程序集中,例如數據表Order對應的實體類OrderInfo,其類圖如下: 

      ps09.gif

      這些對象并不具有持久化的功能,簡單地說,它們是作為數據的載體,便于業務邏輯針對相應數據表進行讀/寫操作。雖然這些類的屬性分別映射了數據表的列,而每一個對象實例也恰恰對應于數據表的每一行,但這些實體類卻并不具備對應的數據庫訪問能力。

      由于數據訪問層和業務邏輯層都將對這些數據實體進行操作,因此程序集Model會被這兩層的模塊所引用。

      第二類數據庫對象則是數據的業務邏輯對象。這里所指的業務邏輯,并非業務邏輯層意義上的領域(domain)業務邏輯(從這個意義上,我更傾向于將業務邏輯層稱為“領域邏輯層”),一般意義上說,這些業務邏輯即為基本的數據庫操作,包括Select,Insert,Update和Delete。由于這些業務邏輯對象,僅具有行為而與數據無關,因此它們均被抽象為一個單獨的接口模塊IDAL,例如數據表Order對應的接口IOrder: 

      ps10.gif

      將數據實體與相關的數據庫操作分離出來,符合面向對象的精神。首先,它體現了“職責分離”的原則。將數據實體與其行為分開,使得兩者之間依賴減弱,當數據行為發生改變時,并不影響Model模塊中的數據實體對象,避免了因一個類職責過多、過大,從而導致該類的引用者發生“災難性”的影響。其次,它體現了“抽象”的精神,或者說是“面向接口編程”的最佳體現。抽象的接口模塊IDAL,與具體的數據庫訪問實現完全隔離。這種與實現無關的設計,保證了系統的可擴展性,同時也保證了數據庫的可移植性。在PetShop中,可以支持SQL Server和Oracle,那么它們具體的實現就分別放在兩個不同的模塊SQLServerDAL、OracleDAL中。

      以Order為例,在SQLServerDAL、OracleDAL兩個模塊中,有不同的實現,但它們同時又都實現了IOrder接口,如圖: 

      ps11.gif

      從數據庫的實現來看,PetShop體現出了沒有ORM框架的臃腫與丑陋。由于要對數據表進行Insert和Select操作,以SQL Server為例,就使用了SqlCommand,SqlParameter,SqlDataReader等對象,以完成這些操作。尤其復雜的是Parameter的傳遞,在PetShop中,使用了大量的字符串常量來保存參數的名稱。此外,PetShop還專門為SQL Server和Oracle提供了抽象的Helper類,包裝了一些常用的操作,如ExecuteNonQuery、ExecuteReader等方法。

      在沒有ORM的情況下,使用Helper類是一個比較好的策略,利用它來完成數據庫基本操作的封裝,可以減少很多和數據庫操作有關的代碼,這體現了對象復用的原則。PetShop將這些Helper類統一放到DBUtility模塊中,不同數據庫的Helper類暴露的方法基本相同,只除了一些特殊的要求,例如Oracle中處理bool類型的方式就和SQL Server不同,從而專門提供了OraBit和OraBool方法。此外,Helper類中的方法均為static方法,以利于調用。OracleHelper的類圖如下: 

      ps12.gif

      對于數據訪問層來說,最頭疼的是SQL語句的處理。在早期的CS結構中,由于未采用三層式架構設計,數據訪問層和業務邏輯層是緊密糅合在一起的,因此,SQL語句遍布與系統的每一個角落。這給程序的維護帶來極大的困難。此外,由于Oracle使用的是PL-SQL,而SQL Server和Sybase等使用的是T-SQL,兩者雖然都遵循了標準SQL的語法,但在很多細節上仍有區別,如果將SQL語句大量的使用到程序中,無疑為可能的數據庫移植也帶來了困難。

      最好的方法是采用存儲過程。這種方法使得程序更加整潔,此外,由于存儲過程可以以數據庫腳本的形式存在,也便于移植和修改。但這種方式仍然有缺陷。一是存儲過程的測試相對困難。雖然有相應的調試工具,但比起對代碼的調試而言,仍然比較復雜且不方便。二是對系統的更新帶來障礙。如果數據庫訪問是由程序完成,在.Net平臺下,我們僅需要在修改程序后,將重新編譯的程序集xcopy到部署的服務器上即可。如果使用了存儲過程,出于安全的考慮,必須有專門的DBA重新運行存儲過程的腳本,部署的方式受到了限制。

      我曾經在一個項目中,利用一個專門的表來存放SQL語句。如要使用相關的SQL語句,就利用關鍵字搜索獲得對應語句。這種做法近似于存儲過程的調用,但卻避免了部署上的問題。然而這種方式卻在性能上無法得到保證。它僅適合于SQL語句較少的場景。不過,利用良好的設計,我們可以為各種業務提供不同的表來存放SQL語句。同樣的道理,這些SQL語句也可以存放到XML文件中,更有利于系統的擴展或修改。不過前提是,我們需要為它提供專門的SQL語句管理工具。

      SQL語句的使用無法避免,如何更好的應用SQL語句也無定論,但有一個原則值得我們遵守,就是“應該盡量讓SQL語句盡存在于數據訪問層的具體實現中”。

      當然,如果應用ORM,那么一切就變得不同了。因為ORM框架已經為數據訪問提供了基本的Select,Insert,Update和Delete操作了。例如在NHibernate中,我們可以直接調用ISession對象的Save方法,來Insert(或者說是Create)一個數據實體對象:

      public void Insert(OrderInfo order)
      {
          ISession s 
      = Sessions.GetSession();
          ITransaction trans 
      = null;
          
      try
          
      {
          trans 
      = s.BeginTransaction();
            s.Save( order);
            trans.Commit();
          }

          
      finally
          
      {
            s.Close();
          }

      }

      沒有SQL語句,也沒有那些煩人的Parameters,甚至不需要專門去考慮事務。此外,這樣的設計,也是與數據庫無關的,NHibernate可以通過Dialect(方言)的機制支持不同的數據庫。唯一要做的是,我們需要為OrderInfo定義hbm文件。

      當然,ORM框架并非是萬能的,面對紛繁復雜的業務邏輯,它并不能完全消滅SQL語句,以及替代復雜的數據庫訪問邏輯,但它卻很好的體現了“80/20(或90/10)法則”(也被稱為“帕累托法則”),也就是說:花比較少(10%-20%)的力氣就可以解決大部分(80%-90%)的問題,而要解決剩下的少部分問題則需要多得多的努力。至少,那些在數據訪問層中占據了絕大部分的CRUD操作,通過利用ORM框架,我們就僅需要付出極少數時間和精力來解決它們了。這無疑縮短了整個項目開發的周期。

      還是回到對PetShop的討論上來。現在我們已經有了數據實體,數據對象的抽象接口和實現,可以說有關數據庫訪問的主體就已經完成了。留待我們的還有兩個問題需要解決:
      1、數據對象創建的管理
      2、利于數據庫的移植

      在PetShop中,要創建的數據對象包括Order,Product,Category,Inventory,Item。在前面的設計中,這些對象已經被抽象為對應的接口,而其實現則根據數據庫的不同而有所不同。也就是說,創建的對象有多種類別,而每種類別又有不同的實現,這是典型的抽象工廠模式的應用場景。而上面所述的兩個問題,也都可以通過抽象工廠模式來解決。標準的抽象工廠模式類圖如下: 

      ps13.gif

      例如,創建SQL Server的Order對象如下:

      1 PetShopFactory factory = new SQLServerFactory();
      2 IOrder = factory.CreateOrder();

       

      要考慮到數據庫的可移植性,則factory必須作為一個全局變量,并在主程序運行時被實例化。但這樣的設計雖然已經達到了“封裝變化”的目的,但在創建PetShopFactory對象時,仍不可避免的出現了具體的類SQLServerFactory,也即是說,程序在這個層面上產生了與SQLServerFactory的強依賴。一旦整個系統要求支持Oracle,那么還需要修改這行代碼為:

      1 PetShopFactory factory = new OracleFactory();

       

      修改代碼的這種行為顯然是不可接受的。解決的辦法是“依賴注入”。“依賴注入”的功能通常是用專門的IoC容器提供的,在Java平臺下,這樣的容器包括Spring,PicoContainer等。而在.Net平臺下,最常見的則是Spring.Net。不過,在PetShop系統中,并不需要專門的容器來實現“依賴注入”,簡單的做法還是利用配置文件和反射功能來實現。也就是說,我們可以在web.config文件中,配置好具體的Factory對象的完整的類名。然而,當我們利用配置文件和反射功能時,具體工廠的創建就顯得有些“畫蛇添足”了,我們完全可以在配置文件中,直接指向具體的數據庫對象實現類,例如PetShop.SQLServerDAL.IOrder。那么,抽象工廠模式中的相關工廠就可以簡化為一個工廠類了,所以我將這種模式稱之為“具有簡單工廠特質的抽象工廠模式”,其類圖如下: 

      ps14.gif

      DataAccess類完全取代了前面創建的工廠類體系,它是一個sealed類,其中創建各種數據對象的方法,均為靜態方法。之所以能用這個類達到抽象工廠的目的,是因為配置文件和反射的運用,如下的代碼片斷所示:

       1 public sealed class DataAccess
       2 {
       3  // Look up the DAL implementation we should be using
       4     private static readonly string path = ConfigurationManager.AppSettings[”WebDAL”];
       5     private static readonly string orderPath = ConfigurationManager.AppSettings[”OrdersDAL”];
       6 
       7  public static PetShop.IDAL.IOrder CreateOrder()
       8  {
       9          string className = orderPath + “.Order”;
      10          return (PetShop.IDAL.IOrder)Assembly.Load(orderPath).CreateInstance(className);
      11     }
      12 }
      13 
      14 

      在PetShop中,這種依賴配置文件和反射創建對象的方式極其常見,包括IBLLStategy、CacheDependencyFactory等等。這些實現邏輯散布于整個PetShop系統中,在我看來,是可以在此基礎上進行重構的。也就是說,我們可以為整個系統提供類似于“Service Locator”的實現:

       1 public static class ServiceLocator
       2 {
       3  private static readonly string dalPath = ConfigurationManager.AppSettings[”WebDAL”];
       4     private static readonly string orderPath = ConfigurationManager.AppSettings[”OrdersDAL”];
       5  //……
       6  private static readonly string orderStategyPath = ConfigurationManager.AppSettings[”OrderStrategyAssembly”];
       7 
       8  public static object LocateDALObject(string className)
       9  {
      10   string fullPath = dalPath + “.” + className;
      11   return Assembly.Load(dalPath).CreateInstance(fullPath);
      12  }
      13 public static object LocateDALOrderObject(string className)
      14  {
      15   string fullPath = orderPath + “.” + className;
      16   return Assembly.Load(orderPath).CreateInstance(fullPath);
      17  }
      18 public static object LocateOrderStrategyObject(string className)
      19  {
      20   string fullPath = orderStategyPath + “.” + className;
      21   return Assembly.Load(orderStategyPath).CreateInstance(fullPath);
      22  }
      23  //……
      24 }
      25 
      26 

      那么和所謂“依賴注入”相關的代碼都可以利用ServiceLocator來完成。例如類DataAccess就可以簡化為:

      1 public sealed class DataAccess
      2 {
      3  public static PetShop.IDAL.IOrder CreateOrder()
      4  {
      5          return (PetShop.IDAL.IOrder)ServiceLocator. LocateDALOrderObject(”Order”);
      6     }
      7 }
      8 

      通過ServiceLocator,將所有與配置文件相關的namespace值統一管理起來,這有利于各種動態創建對象的管理和未來的維護。

      posted @ 2007-07-10 23:53  SapronLee  閱讀(304)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日韩午夜福利片段在线观看| 风流老熟女一区二区三区| 在线天堂中文新版www| 77se77亚洲欧美在线| 久久精品国产亚洲AV瑜伽| 武山县| 香港特级三A毛片免费观看| 色婷婷欧美在线播放内射| 亚洲国产成人久久综合野外| 果冻传媒一区二区天美传媒| 免费无码午夜福利片| 色翁荡息又大又硬又粗又视频图片| 又黄又爽又色的少妇毛片| 国产毛片子一区二区三区| 哈尔滨市| 亚洲精品一区二区区别| 国产中文三级全黄| 日韩在线视频线观看一区| 国产亚洲精品第一综合另类无码无遮挡又大又爽又黄的视频 | 亚洲中文字幕无码av永久| 亚洲av不卡电影在线网址最新| 无码专区—va亚洲v天堂麻豆| 91亚洲国产成人精品性色| 国内精品久久人妻无码不卡| 97人人添人人澡人人澡人人澡| 亚洲线精品一区二区三八戒 | 高潮射精日本韩国在线播放| 国产亚洲999精品AA片在线爽| 亚洲 欧美 清纯 校园 另类| 久久狠狠一本精品综合网| 亚洲高清成人av在线| 国产欧美综合在线观看第十页| 成年午夜免费韩国做受视频| 吃奶还摸下面动态图gif| 人人澡人人透人人爽| 国产v亚洲v天堂a无码| 综合区一区二区三区狠狠| 亚洲 制服 丝袜 无码| 亚洲熟妇精品一区二区| 超碰成人人人做人人爽| 精品久久人人做爽综合|