日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图解“管道过滤器模式”应用实例:SOD框架的命令执行管道

發(fā)布時間:2024/3/12 编程问答 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图解“管道过滤器模式”应用实例:SOD框架的命令执行管道 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

管道和過濾器

管道和過濾器是八種體系結(jié)構(gòu)模式之一,這八種體系結(jié)構(gòu)模式是:層、管道和過濾器、黑板、代理者、模型-視圖-控制器(MVC) 表示-抽象-控制(PAC)、微核、映像。

管道和過濾器適用于需要漸增式處理數(shù)據(jù)流的領(lǐng)域,而常見的“層”模式它 能夠被分解成子任務(wù)組,其中每個子任務(wù)組處于一個特定的抽象層次上。

按照《POSA(面向模式的軟件架構(gòu))》里的說法,管道過濾器(Pipe-And-Filter)應(yīng)該屬于架構(gòu)模式,因為它通常決定了一個系統(tǒng)的基本架構(gòu)。管道過濾器和生產(chǎn)流水線類似,在生產(chǎn)流水線上,原材料在流水線上經(jīng)一道一道的工序,最后形成某種有用的產(chǎn)品。在管道過濾器中,數(shù)據(jù)經(jīng)過一個一個的過濾器,最后得到需要的數(shù)據(jù)。

管道&過濾器模型的基本部件都有一套輸入輸出接口。每個部件從輸入接口中讀取數(shù)據(jù),經(jīng)過處理,將結(jié)果數(shù)據(jù)置于輸出接口中,這樣的部件稱為“過濾器”。這種模型的連接者將一個過濾器的輸出傳送到另一個過濾器的輸入,
我們把這種連接者稱為“管道”。在這種模型中,過濾器必須是獨立的實體,每一個過濾器的狀態(tài)不受其它過濾器的影響,并且,雖然人們對過濾器的輸入輸出有一定的規(guī)定,但過濾器并不需要知道向它提供數(shù)據(jù)流的過濾器和
它要提供數(shù)據(jù)流的過濾器的內(nèi)部細節(jié)。任何兩個過濾器,只要它們之間傳送的數(shù)據(jù)遵守共同的規(guī)約就可以相連接。
每個過濾器都有自己獨立的輸入輸出接口,如果過濾器間傳輸?shù)臄?shù)據(jù)遵守其規(guī)約,只要用管道將它們連接就可以正常工作。

查詢的關(guān)注點

基于以上管道和過濾器特點,它為處理數(shù)據(jù)流的系統(tǒng)提供了一種良好的結(jié)構(gòu),每一個處理步驟封裝在一個過濾器組件中,數(shù)據(jù)通過相鄰的過濾器之間的管道傳輸。在程序處理中,也有類似的這種數(shù)據(jù)流,最常見的就是命令處理的數(shù)據(jù)流,它從最開始的查詢命令,到最后的結(jié)果輸出,會經(jīng)過多個步驟,以ADO.NET來說,執(zhí)行一個查詢會經(jīng)過以下過程:

查詢命令:

  • 獲取數(shù)據(jù)集:
  • 打開數(shù)據(jù)庫連接 IDbConnection
  • 創(chuàng)建命令對象 IDbCommand
  • 創(chuàng)建數(shù)據(jù)適配器 IDataAdapter
  • 填充數(shù)據(jù)集 IDataAdapter.Fill(DataSet)
  • 關(guān)閉數(shù)據(jù)庫連接
  • 返回數(shù)據(jù)集 DataSet?
    • 獲取數(shù)據(jù)閱讀器
  • 打開數(shù)據(jù)庫連接 IDbConnection
  • 創(chuàng)建命令對象 IDbCommand
  • 執(zhí)行數(shù)據(jù)閱讀器查詢 IDbCommand.ExecuteReader
  • 返回數(shù)據(jù)閱讀器 IDataReader
  • 關(guān)閉數(shù)據(jù)庫連接
  • ?非查詢命令:

  • 打開數(shù)據(jù)庫連接 IDbConnection
  • 創(chuàng)建命令對象 IDbCommand
  • 執(zhí)行查詢 IDbCommand.ExecuteNonQuery()
  • 關(guān)閉數(shù)據(jù)庫連接?
  • 可以看到,上面這幾種查詢命令的執(zhí)行,都要經(jīng)過幾個相同的步驟:打開數(shù)據(jù)庫連接,創(chuàng)建命令對象,執(zhí)行查詢,返回結(jié)果,關(guān)閉數(shù)據(jù)庫連接,這幾個步驟是有嚴格順序的,前后依賴的,就像水流一般,因此,我們也可以利用“管道--過濾器”模式,在查詢命令的執(zhí)行過程中,插入某些特定的處理邏輯。 從最終使用者的角度來說,一個查詢有4個關(guān)注點:
    • 查詢前
    • 查詢中
    • 查詢后
    • 查詢異常
    其中,查詢中是ADO.NET等數(shù)據(jù)訪問組件內(nèi)部的處理過程,一般不能直接提供用戶可以切入和干預的觀察點,那么剩下3個關(guān)注點,就是我們可以用的,這3個關(guān)注點,就像一個水管的三個閥門一樣。

    SOD框架的命令處理管道

    命令處理接口

    SOD框架現(xiàn)在也提供了這樣的三個關(guān)注點,使得使用組件的用戶,能夠無需修改組件內(nèi)部的代碼,改變和觀察組件的處理情況,這三個關(guān)注點對應(yīng)的是 ICommandHandle接口的3個方法: /// <summary>/// 查詢命令處理器接口/// </summary>public interface ICommandHandle{/// <summary>/// 獲取當前適用的數(shù)據(jù)庫類型,如果通用,請設(shè)置為 UNKNOWN/// </summary>DBMSType ApplayDBMSType { get; }/// <summary>/// 執(zhí)行前處理,比如預處理SQL,補充設(shè)定參數(shù)類型,返回是否繼續(xù)進行查詢執(zhí)行/// </summary>/// <param name="db">數(shù)據(jù)庫訪問對象</param>/// <param name="SQL"></param>/// <param name="commandType"></param>/// <param name="parameters"></param>/// <returns>返回真,以便最終執(zhí)行查詢,否則將終止查詢</returns>bool OnExecuting(CommonDB db, ref string SQL, CommandType commandType, IDataParameter[] parameters);/// <summary>/// 執(zhí)行過程中出錯情況處理/// </summary>/// <param name="cmd"></param>/// <param name="errorMessage"></param>void OnExecuteError(IDbCommand cmd, string errorMessage);/// <summary>/// 查詢執(zhí)行完成后的處理,不管是否執(zhí)行出錯都會進行的處理/// </summary>/// <param name="cmd"></param>/// <param name="recordAffected">命令執(zhí)行的受影響記錄行數(shù)</param>long OnExecuted(IDbCommand cmd, int recordAffected);}

    ?

    一圖勝千言,先看下面的“SOD框架命令處理管道”圖:

    ?

    ?由前面接口的定義并結(jié)合這個圖,可以看到查詢命令在“數(shù)據(jù)訪問”這個管道里面流動過程:

    • 首先,它在 OnExecuting 這個過濾插口位置改變命令的行為特征,比如SQL預處理,終止查詢等,發(fā)起異步操作等;
    • 接著,查詢命令由Ado.Net進行處理,而此時是很有可能發(fā)生查詢錯誤的情況的,那么提供一個OnExecuteError 過濾插口,讓錯誤信息可以被一些過濾器使用,比如查詢操作日志組件;
    • 最后,不論前面命令執(zhí)行是否成功,命令執(zhí)行完了還需要進行一些其它的處理,那么提供一個OnExecuteError 過濾插口,比如觀察命令執(zhí)行的結(jié)果行/影響行,命令的執(zhí)行時間,返回異步通知等。

    ?根據(jù)這里定義的命令執(zhí)行管道接口,最典型的實現(xiàn)就是可以用來記錄查詢?nèi)罩?#xff0c;比如下面的 CommandExecuteLogHandle 類:

    /// <summary>/// 命令執(zhí)行日志處理器,可以記錄SQL和參數(shù),執(zhí)行時間等信息/// </summary>public class CommandExecuteLogHandle :ICommandHandle{/// <summary>/// 初始化一個命令執(zhí)行日志處理器/// </summary>public CommandExecuteLogHandle(){this.CurrCommandLog = new CommandLog(true);//這里需要進行一些初始化檢查,設(shè)置日志路徑等if (CommandLog.DataLogFile == null)CommandLog.DataLogFile = "~/sql.log";CommandLog.SaveCommandLog = true;}public CommandLog CurrCommandLog { get; private set; }public bool OnExecuting(CommonDB db, ref string SQL, CommandType commandType, IDataParameter[] parameters){this.CurrCommandLog.ReSet();return true;}public void OnExecuteError(IDbCommand cmd, string errorMessage){CurrCommandLog.WriteErrLog(cmd, "AdoHelper:" + errorMessage);}public long OnExecuted(IDbCommand cmd, int recordAffected){long elapsedMilliseconds;CurrCommandLog.WriteLog(cmd, "AdoHelper", out elapsedMilliseconds);CurrCommandLog.WriteLog("RecordAffected:"+recordAffected , "AdoHelper");return elapsedMilliseconds;}public DBMSType ApplayDBMSType{get { return DBMSType.UNKNOWN; }
    }}

    注意,這里 ApplayDBMSType 返回 UNKNOW,表示當前接口實現(xiàn)類性適合于任意數(shù)據(jù)庫查詢的情況。

    另外,日志過濾器內(nèi)部使用了框架內(nèi)置的 CommandLog 類,它可以異步的記錄SQL執(zhí)行情況,并能記錄查詢時間大于某個值的查詢,詳細請看《PDF.NET的SQL日志》。

    ?

    再看下面,我們實現(xiàn)一個用于處理Oracle查詢的“過濾器”組件,它會在查詢開始前,對SQL進行一些預處理,比如將本來使用于SQLSERVER的SQL語句格式,處理成Oracle特有的格式:

    /// <summary>/// 自定義的Oracle命令處理器,用于處理特殊的字段名大寫問題/// </summary>public class OracleCommandHandle : ICommandHandle{public bool OnExecuting(CommonDB db, ref string sql, System.Data.CommandType commandType, System.Data.IDataParameter[] parameters){sql= sql.Replace("[", "").Replace("]", "").Replace("@", ":").ToUpper();//設(shè)置SQLSERVER兼容性為假,避免命令對象真正執(zhí)行的時候再進行Oracle的查詢語句的預處理。db.SqlServerCompatible = false;//返回真,以便最終執(zhí)行查詢,否則將終止查詢return true;}public void OnExecuteError(System.Data.IDbCommand cmd, string errorMessage){}public long OnExecuted(System.Data.IDbCommand cmd, int recordAffected){return 1;}public PWMIS.Common.DBMSType ApplayDBMSType{get { return PWMIS.Common.DBMSType.Oracle; }}}

    ?注意:上面這個實現(xiàn)類,指明了當前命令執(zhí)行過濾器組件,僅使用于Oracle數(shù)據(jù)庫,當前如果是其它數(shù)據(jù)庫類型,會忽略該過濾器組件。

    除此之外,是不是還可以寫一個過濾器組件,監(jiān)視下當前查詢是否執(zhí)行成功,如果成功,將查詢的SQL和參數(shù)發(fā)送到消息隊列,進行異步更新其它數(shù)據(jù)庫?

    開閉原則

    所以,SOD框架的“命令執(zhí)行管道”給予了最終用戶在不改變原有數(shù)據(jù)訪問組件的內(nèi)部實現(xiàn)的情況下,一個監(jiān)視和處理命令執(zhí)行過程的“窗口”,一個或者多個對查詢命令的“過濾器”組件,這正是面向?qū)ο笤瓌t之一的開閉原則

    我們來看下百度百科對開閉原則的解釋:

    開閉原則(OCP)是面向?qū)ο笤O(shè)計中“可復用設(shè)計”的基石,是面向?qū)ο笤O(shè)計中最重要的原則之一,其它很多的設(shè)計原則都是實現(xiàn)開閉原則的一種手段。遵循開閉原則設(shè)計出的模塊具有兩個主要特征:(1)對于擴展是開放的(Open for extension)。這意味著模塊的行為是可以擴展的。當應(yīng)用的需求改變時,我們可以對模塊進行擴展,使其具有滿足那些改變的新行為。也就是說,我們可以改變模塊的功能。(2)對于修改是關(guān)閉的(Closed for modification)。對模塊行為進行擴展時,不必改動模塊的源代碼或者二進制代碼。模塊的二進制可執(zhí)行版本,無論是可鏈接的庫、DLL或者.EXE文件,都無需改動。

    ?

    既然命令執(zhí)行管道如此有用,我們該如何使用呢?還是直接看示例代碼比較簡單:

    /// <summary>/// 用來測試的本地 數(shù)據(jù)庫上下文類/// </summary>public class MyOracleDbContext : DbContext {public MyOracleDbContext(): base("local"){//local 是連接字符串名字//注冊日志處理器和Oracle命令處理器base.CurrentDataBase.RegisterCommandHandle(new CommandExecuteLogHandle());base.CurrentDataBase.RegisterCommandHandle(new OracleCommandHandle());}#region 父類抽象方法的實現(xiàn)protected override bool CheckAllTableExists(){//創(chuàng)建用戶表CheckTableExists<User>();return true;}#endregion}

    在這個?MyOracleDbContext 類中,我們注冊了2個過濾器組件:日志過濾器和Oracle命令過濾器。

    如果當前連接配置名 local 對應(yīng)的數(shù)據(jù)庫訪問提供程序不是Oracle了怎么辦?

    不用擔心,前面說過,?Oracle命令過濾器僅對Oracle數(shù)據(jù)訪問有效,其它數(shù)據(jù)庫訪問會忽略,而日志過濾器組件它是適用于任何數(shù)據(jù)庫訪問的。

    上面的示例代碼中,CurrentDataBase 對象其實就是 SOD框架的 AdoHelper對象,所以,只要你使用SOD框架,那么不管你使用的是框架的ORM,SQL-MAP,Data Controls功能,甚至是最簡單的“SqlHelper”類應(yīng)用,你都可以享受到SOD框架的“命令執(zhí)行管道”帶給你d便利!

    與“觀察者模式”的區(qū)別

    .NET框架中,對觀察者模式最常見的實現(xiàn)就是“事件”,事件可以實現(xiàn)監(jiān)視某個對象的改變情況然后發(fā)起事件通知,最后由事件處理程序完成處理。在本文描述的查詢處理場景中,也可以在查詢處理前,處理后,發(fā)生異常這3個“觀察點”發(fā)起事件,并且,事件也可以實現(xiàn)“多播”,一個事件可以由多個事件處理程序來處理。所以,從這個意義上來說,“管道-過濾器”模式跟“觀察者”模式功能上很相似的,但為何SOD框架不選擇后者來實現(xiàn)呢?

    我認為,主要區(qū)別有以下幾個方面:

    在架構(gòu)層面上,

    “管道-過濾器”模式通常用于架構(gòu)設(shè)計層面,是一種“架構(gòu)模式”,比如分層架構(gòu);而觀察者模式一種面向?qū)ο缶幊痰哪J?#xff0c;運用的領(lǐng)域不一樣。

    “管道-過濾器”模式讓架構(gòu)實現(xiàn)松耦合;而觀察者模式的觀察者和被觀察者之間,往往是緊密耦合的關(guān)系。

    在具體使用形式上,

    “架構(gòu)模式”可以通過配置文件來提供附件的一種功能實現(xiàn),比如ASP.NET的HttpHandle,ASP.NET MVC的Controller上的Filter等,所以它的實現(xiàn)是松耦合的;

    而觀察者模式往往體現(xiàn)在編寫的代碼中,用事件來處理代碼來實現(xiàn),所以它往往是緊耦合的。

    在業(yè)務(wù)語義上,

    “管道-過濾器”是用于處理流動的載體的,比如數(shù)據(jù),信息或其它具有流動特性的物體,方便進行多環(huán)節(jié),多層次的攔截或者加工處理,并且每個處理環(huán)節(jié)都有序的,流動和有序,這是這類業(yè)務(wù)最重要的特征;

    “事件”處理的客體范圍更廣,事件的客體沒有固定的形態(tài),事件的發(fā)生和處理可能都是無序的。

    ?

    其它方面的考慮,事件使用前總是需要聲明事件掛鉤,會多增加一些代碼量,并且使用完成之后,往往還需要解除掛鉤,否則可能發(fā)生內(nèi)存泄漏,請參見 我另外一篇文章《Release編譯模式下,事件是否會引起內(nèi)存泄漏問題初步研究》。

    總結(jié)

    所以,在當前這個數(shù)據(jù)查詢的場景中,對于查詢命令的處理,采用“管道-過濾器”模式來實現(xiàn)一個命令執(zhí)行管道,是最合適的,它讓人在業(yè)務(wù)語義上更加明確,并且使用上更加靈活,代碼實現(xiàn)量也最小,而且不需要修改原有的代碼實現(xiàn),符合開閉原則。

    到目前為止,我還沒有看到其它 數(shù)據(jù)處理框架/ORM框架 比較明確的提供了關(guān)注和干預組件內(nèi)部查詢執(zhí)行過程的功能,都只能進行外部的攔截,如果你有這樣的需求,來試試SOD框架帶給你的靈活和自由吧!

    附注:

    SOD不僅僅是一個ORM,它還有SQL-MAP和DataControl,具體可以看框架官網(wǎng)?http://www.pwmis.com/sqlmap?,9年歷史鑄就的成果,堅固可靠。

    非常感謝你看到這里,相信你初步了解了SOD框架的基本功能,如果您還有其它問題,歡迎你在項目的開源網(wǎng)站?http://pwmis.codeplex.com的討論區(qū)發(fā)帖,或者去官方博客相關(guān)文章回帖也可。

    ?



    ? ? 本文轉(zhuǎn)自深藍醫(yī)生博客園博客,原文鏈接:http://www.cnblogs.com/bluedoctor/p/5278995.html,如需轉(zhuǎn)載請自行聯(lián)系原作者



    總結(jié)

    以上是生活随笔為你收集整理的图解“管道过滤器模式”应用实例:SOD框架的命令执行管道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。