亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

為什么 FleaPHP 使用 Table Data Gateway 代替

系統 2674 0

[原文地址: http://www.dualface.com/blog/?p=357 ]

  許多開發者很疑惑為什么 FleaPHP 以高效開發為目標,卻沒有提供 Active Record 模式。本文嘗試詳細闡述這個問題。

Active Record 是什么?

  Active Record 模式中文名為“活動記錄”,在《企業應用架構模式》(PoEAA)一書中定義如下:

活動記錄(Active Record):一個對象,它包裝數據庫表或視圖中的某一行,封裝數據庫訪問,并在這些數據上增加了領域邏輯。

  舉個例子來說,一個圖書數據表,每一條記錄就是一本圖書的信息。那么采用 Active Record 時,每一本圖書就是一個 Active Record 對象實例。

Active Record 因 Ruby On Rails 而流行

  Active Record 之所以現在這么炙手可熱,甚至許多人將 Active Record 和 ORM 劃等號,完全是 Ruby On Rails 的原因。

在 Ruby On Rails 中,Active Record 除了最基本的將數據記錄和一個對象互相映射外,還提供了數據(而不是對象)間關聯關系的處理 。例如:

  一本圖書有一個或者多個作者,所以每一個圖書對象都和多個作者對象關聯。反過來一個作者可以寫多本書,所以一個作者對象也和多個圖書對象關聯。

  在 RoR 中,我們獲取一個圖書對象時,自動就獲得了該圖書對象所對應的作者對象(本質上是圖書數據對應的作者數據)。更進一步,通過圖書對象關聯的作者對象,我們 可以獲取該作者所寫的所有圖書的對象實例。而這些工作,在 RoR 中只需要幾行代碼而已,以前我們需要寫上一大段代碼才能實現同樣的效果。

  RoR 中,對 Active Record 模式的實現完全利用了 Ruby 語言的靈活性,簡短幾行代碼就可以定義一個關聯。并且通過復雜的 ActiveRecord:Base 對象,提供了 CRUD(創建、讀取、更新、刪除)操作的默認處理。所以使用 RoR 時,絕大部分常見的數據庫操作只需要很少量的代碼就可以完成,大大提高了開發效率。

  但 Active Record 模式也不是完美的,Active Record 存在不少缺點。

  • Active Record 模式需要數據表結構和對象屬性一一對應(至少是大部分對應),否則將難以使用 Active Record 模式;
  • Active Record 模式并不能夠真正適合完全面向對象的應用程序。因為 Active Record 模式本質上就要求一個對象必須和一個數據表對應。但在完全面向對象的應用程序中,數據和操作數據的方法很可能分布在各個不同的對象中,這些對象卻并沒有和 某一個數據表完全對應,而且 Active Record 無法很好的處理對象的繼承、聚合等面向對象常見的對象間關系;
  • 隨著逐漸向 Active Record 添加業務邏輯,Active Record 對象中會混入越來越多的 SQL 語句,這在更復雜的項目中顯然是一個不利因素。

  如果在 Active Record 模式中添加了對數據關系(注意,不是對象關系)的處理,那么還要注意性能問題:
  假如一個 Active Record 對象有多個關聯。那么我取出一個對象時,很可能就連帶取出了其他不少對象。但這些對象可能根本就是本次操作用不上的。其次,將對象更新到數據庫時,也需要對關聯的對象進行處理,否則對關聯對象的修改就會丟失。

  雖然可以用各種技巧來避免這些情況,但毫無疑問需要開發者對 RoR 的 Active Record 很熟悉才行。否則看上去很簡單的代碼,背后則會是噩夢般的數據庫操作。

  其次,假設我們要將數據庫中每本書的單價減半,那么采用 Active Record 模式時,就必須首先讀取所有的記錄并實例化為對象,然后更新對象屬性,再寫回數據庫。可想而知這樣會有多差的效率。
  當然了,實際開發中沒有人會這樣做。開發者會編寫一個單獨的方法,用一條 SQL 語句完成對批量數據的更新。但也說明 Active Record 模式不適合批量處理數據,而現實世界中,批量處理數據的需求隨處可見。

  不過由于 RoR 對開發效率戲劇性的提高,所以對于追求開發效率的項目,RoR 是一個很不錯的選擇。而且性能上的不足可以通過更新硬件或者配合其他技術手段來改善(例如 FastCGI 通常是運行 RoR 應用的首選)。因此在現實世界中, 37signals.com 公司的所有基于 RoR 開發的應用,都獲得了良好的性能表現(但是同等的硬件,跑 PHP 開發的同樣功能應用是更好還是更差呢?這個問題沒有答案)。

Active Record 與 ORM

  許多人將 Active Record 與 ORM 劃等號,這是錯誤的。ORM(對象關系映射)是將對象及對象間的關系(繼承、聚合等)映射到關系式數據庫中。由于面向對象和關系式數據庫天生的不匹配,所以這種映射是相當復雜的。

  而 Active Record 原本只是將一個數據行記錄包裝為一個對象,只是在 RoR 中由于添加了對關系的處理,而具有了一些 ORM 的特征。所以可以簡單的將 RoR 中的 Active Record 看作 ORM 的一種實現方式。但本質上,RoR 中的 Active Record 是處理數據間的關系而不是對象間的關系(但支持對象繼承),因為每一個 Active Record 對象都是和數據表一一對應的。

  那為什么在 Java 世界中,沒有大量采用 Active Record 模式呢?

  在 Java 世界中,絕大部分 ORM 都是作為中間件存在的。由于 Java 與 Ruby、PHP 等腳本語言截然不同的運行機制。所以即便是很復雜的中間層,只要能夠在運行時提供良好的性能,那就能夠被開發者接受。而 Hibernate 這樣的 ORM 中間件能夠提供比 Active Record 多得多的功能和靈活性,所以 Active Record 模式在 Java 世界不受歡迎就可以理解了。

  而在 .NET 世界中,大量使用的都是表數據入口(Table Data Gateway)和表模塊(Table Module)。這兩種模式由于有 Microsoft 出色的 IDE 支持,所以能夠獲得很高的開發效率,自然 .NET 開發者對 Active Record 模式也不感興趣了。

如果將 Active Record 或者 ORM 照搬到 PHP 中呢?

  許多開發者都很羨慕 Hibernate 的強大功能和 RoR 中 Active Record 的快速開發能力,但是這些東西如果照搬到 PHP 中,會遇到一個相當大的麻煩:

  PHP 本質上是解釋執行的腳本語言,所以對于每一次 HTTP 請求,PHP 執行環境都會將請求的 .php 文件編譯為 opcode,然后執行 opcode,再清理所有的資源(內存、數據庫連接、文件句柄等等)。在這種環境中,應用程序應該花盡可能少的時間去初始化底層框架,而是把大部分資源用 在業務邏輯的執行上。

  但 Ruby 也是解釋執行,為什么就可以用 Active Record,而 PHP 就不應該呢?
簡單點說就是因為 PHP 在面向對象支持上的缺陷使得要實現和 RoR 同等功能 的 Active Record 模式變得非常艱難。也許你對此不以為然,那么可以實際嘗試一下使用 PHP on Trax(一個 RoR 的 PHP 克隆)。看看一次簡單的讀取操作需要載入多少文件并調用多少對象和方法。

  所以有些 PHP 框架提供的 Active Record 模式實現非常簡單,根本不考慮關聯問題,但這樣一來使用 Active Record 能獲得的開發效率提升就太小了。

  至于更為復雜的 ORM,目前 PHP 領域還沒有一個真正的成功項目。雖然 Propel 是目前 PHP 領域唯一一個具有實際工作能力的 ORM。但由于其自身的復雜性和執行效率問題,一直沒有得到廣泛使用。即便是 Symfony 也是對 Propel 進行裁剪后才用于處理數據庫操作。

雖然在國內 PHP 社區中常看到有人說自己做的 ORM 如何如何先進,既有高級特征,又有好的效率。但自始至終沒有看到過有人公布代碼。至于不公布的原因不外乎:還不夠成熟,成熟后再公布;我是最領先的,除非 有了同水平的,不然我不會公布;商業產品,不能泄露。而且別說是代碼,就算問問實現原理通常也只能得到幾句無關痛癢的回答。

所以如果你看到這篇文章后,覺得你實現了我認為很難實現的東西,請拿出實際證據。不要再搬出諸如此類的理由,沒有論據的辯論是毫無意義的。

  那么 PHP 就注定和 Active Record 和 ORM 無源嗎?
如果這個問題的潛在意思是問:PHP 就不能找到和 Active Record 一樣好用的數據庫訪問方法嗎?那么答案是否定的。

Table Data Gateway 是一個更合理的選擇

  我仔細研究了 PoEAA 中關于表數據入口、表模塊的內容后,又做了大量實際測試。最終決定在 FleaPHP 中采用 Table Data Gateway(表數據入口)模式來提供數據庫服務。并在此基礎上實現對關聯數據的自動處理。

表數據入口(Table Data Gateway):充當數據表訪問入口的對象,一個實例處理表中所有的行。

表模塊(Table Module):處理某一數據庫表或視圖中所有行的業務邏輯的一個實例。

  表數據入口是封裝一個數據表的操作,而不是一個記錄行。這樣一來,表數據入口可以很方便的處理針對單個記錄和多個記錄的操作,而操作的數據就是 PHP 中的數組。實際上我初期還寫了一些對象來封裝記錄集(也就是多行記錄),不過后來發現完全是多此一舉。PHP 的數組功能非常強大,再專門用對象包裝一下弊大于利。

  針對數據表提供單純的 CRUD 操作吸引力還不夠,所以我在表數據入口的基礎上增加了對 HasOne、HasMany、ManyToMany 以及 BelongsTo 關聯的處理。這四種關聯,基本上滿足了常見的數據關聯操作。

  不過有了自動化的關聯,類似 RoR ActiveRecord 中加載過量數據的問題依然存在,所以 FleaPHP 的表數據入口對象 FLEA_Db_TableDataGateway 也提供了針對關聯的方法,讓開發者可以細粒度的控制數據庫操作。

  而且由于表數據入口是針對純數據進行操作,而不是針對包裝了數據的對象。所以開發者可以很容易的優化數據庫操作,例如無需讀取即可更新數據或者一次性處理大批量的數據。

  相對于 Active Record 模式,Table Data Gateway 模式有下列優勢:

  • 表數據入口針對一個表封裝數據庫操作,這更接近傳統 PHP 開發的思維模式;
  • 處理批量數據時,表數據入口更方便,常見操作無需額外編寫處理方法;
  • 數據以數組的形式保存和傳遞,比將每個記錄行實例化為對象具有好得多的性能;
  • 實現比 Active Record 簡單,每個操作執行更少的代碼;
  • 可以很好的與表模塊(Table Module)模式配合來封裝業務邏輯。從而避免了 Active Record 中將數據庫操作和業務邏輯寫在一起的問題。

  當然,表數據入口也有相對于 Active Record 不足的地方:

  • 由于表數據入口總是傳遞純數據,所以無法像 Active Record 一樣以屬性的形式封裝對數據的操作。不過這種操作即便使用 Active Record 也要多寫不少處理代碼,而使用表數據入口時,這部分代碼只不過是轉移到了表模塊中;
  • 看上去更沒有那么面向對象。可惜的是即便采用 Active Record,大多數應用程序從設計思想上也不是面向對象的,只不過用了一個對象來傳遞數據而已。

  而且 Active Record 存在的一些問題,Table Data Gateway 依然無法避免。最主要的就是表數據入口和表模塊都是和數據表一一對應,因此不適用于持久化細粒度對象。不過熟悉 .NET 的開發者應該很容易找到解決辦法,那就是以表模塊完成大部分業務操作,而細粒度對象僅用于部分操作。這是因為 Microsoft 的開發環境一向都對表數據入口和表模塊有著偏好和最好的支持。

  不過使用表數據入口,相對于 Active Record 最大的好處就是能夠很容易的將業務邏輯操作從表數據入口對象分離到表模塊對象中,因此對于更大更復雜的項目,表數據入口配合表模塊的方式具有更高的可維護性。

表數據入口和表模塊的配合

  表數據入口封裝了針對數據表的操作,而表模塊則封裝了針對數據表的業務邏輯,兩者怎么配合呢?我們就以操作圖書記錄為例,看看具體如何做。

  首先,從 FLEA_Db_TableDataGateway 派生一個類,作為圖書表的表數據入口對象,例如 TableBooks。接下來建立一個空白的類,名為 ModuleBooks。

  1. class ? TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3. ? ? // 只需要指明數據表名稱和主鍵字段名即可,CRUD 操作已經有了默認實現
  4. ? ? var ? $tableName = ' books ' ;
  5. ? ? var ? $primaryKey = ' book_id ' ;
  6. }
  7. ?
  8. class ? ModuleBooks
  9. {
  10. ? ? var ? $table ;
  11. }

  現在我們要統計指定年份的出版的圖書。

Step1: 在 TableBooks 中增加一個方法 countBooksRange():

  1. class ? TableBooks extends FLEA_Db_TableDataGateway
  2. {
  3. ? ? ......
  4. ? ?
  5. ? ? /**
  6. ? ?? * 統計指定時間區間的圖書總數
  7. ? ?? */
  8. ? ? function ? countBooksRange ( $begin , $end )
  9. ? ? {
  10. ? ? ? ? // 對參數進行轉義,確保不會存在 SQL 攻擊漏洞
  11. ? ? ? ? $begin = $this -> _dbo -> qstr ( $begin ) ;
  12. ? ? ? ? $end = $this -> _dbo -> qstr ( $end ) ;
  13. ? ? ? ? return ? $this -> findCount ( " publish_date >= {$begin} AND publish_date <= {$end} " ) ;
  14. ? ? }
  15. }

  countBooksRange() 方法可以統計指定區間的圖書總數,所以我們再給 ModuleBooks 增加一個 countBooksByYear() 方法來統計指定年份的圖書。

  1. class ? ModuleBooks
  2. {
  3. ? ? ......
  4. ? ?
  5. ? ? function ? countBooksByYear ( $year )
  6. ? ? {
  7. ? ? ? ? $begin = date ( " {$year} /1/1 " ) ;
  8. ? ? ? ? $end = date ( " {$year} /12/31 " ) ;
  9. ? ? ? ? return ? $this -> table -> countBooksRange ( $begin , $end ) ;
  10. ? ? }
  11. }

  上面的例子雖然簡單,但是很清晰的描述了表數據入口如何封裝具體的數據庫操作,而表模塊又如何利用表數據入口的方法提供更高層的接口。如果需要 可運行的示例程序,可以參考 FleaPHP 的 SHOP 示例。這個示例中,Model 目錄下就是表模塊,而 Table 目錄下就是表數據入口。

?

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1442087


為什么 FleaPHP 使用 Table Data Gateway 代替 Active Record 來提供數據庫訪問服務


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产午夜精品一区二区 | 波多野结中文字幕在线69视频 | 国产精品亚洲一区二区三区正片 | 欧美ⅹxxxx视频 | 天天干干干干 | 亚洲区精品久久一区二区三区 | 欧美激情视频一区 | 国内夫妇精品对白在线播放 | 国产午夜永久福利视频在线观看 | 中文字幕亚洲日韩无线码 | 中文字幕 亚洲一区 | 日韩区欧美区 | 国产精品国产三级国产无毒 | 久久亚洲精品永久网站 | 精品久久久久久久一区二区手机版 | 日本久久精品免视看国产成人 | 日韩欧美中文在线 | 亚洲国产精品区 | 66av99精品福利视频在线 | 五月婷婷激情在线 | 一级特黄aaa大片 | 国产伦一区二区三区四区久久 | 日韩精品首页 | 天天擦天天干 | 色综合久久一本首久久 | 亚洲成人福利在线 | 免费特黄一级欧美大片 | 女人夜色黄网在线观看 | 久久这里有精品视频任我鲁 | 最新国产午夜精品视频不卡 | 日本高清视频一区二区 | 日韩毛片网 | 草草国产成人免费视频 | 国产精品美女久久久久网站 | 神马九九 | 男人与牛做爰的视频 | 亚洲精品一区二区在线观看 | 国产成人a大片大片在线播放 | 国产精片| 欧美成人xx免费视频 | 欧美 亚洲 激情 |