• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 3, 文章 - 0, 評(píng)論 - 16, 引用 - 0
            數(shù)據(jù)加載中……

            在C++中使用IoC及DSM框架

            自Web和Java橫空出世以來(lái),軟件開(kāi)發(fā)技術(shù),概念以及架構(gòu)的演變翻新有點(diǎn)讓人眼花繚亂,甚至應(yīng)接不暇。然而,恪守技術(shù) 含量至上遺風(fēng)的C++鐵桿精英們似乎是其中的另類(lèi),與標(biāo)新立異擯棄繁文褥節(jié)的Java,Ruby新生代之間儼如隔世。不用說(shuō)DSM,就連IoC這個(gè)起源于 C++年代甚至C++土壤的概念和方法雖然在墻外其他晚輩語(yǔ)言部落中廣受青睞,在C++紅墻內(nèi)卻反而遭長(zhǎng)期冷落。IoC和DSM技術(shù)雖然看似簡(jiǎn)單無(wú)比平淡 無(wú)奇遍地都是,甚至被很多C++大老們嗤之以鼻(甚至斥責(zé)為狗皮膏藥),但卻能大大提升C++軟件開(kāi)發(fā)的效率及質(zhì)量,而且能徹底簡(jiǎn)化和凈化很多繁瑣丑陋甚至危險(xiǎn)的C++原始解決 方案。比如,由IBM領(lǐng)頭忽悠的一個(gè)所謂“服務(wù)組件架構(gòu)”SCA(Service Component Architecture)的廠(chǎng)商標(biāo)準(zhǔn),其C++組件容器的參考實(shí)現(xiàn)(RI)雖由IBM幾個(gè)老大全時(shí)操刀,歷時(shí)兩年卻仍然困難重重地蹣跚在Apache孵 化項(xiàng)目階段。不僅如此,這個(gè)沿用傳統(tǒng)思維和方法的參考實(shí)現(xiàn)使用起來(lái)也相當(dāng)繁瑣,甚至危險(xiǎn)(用戶(hù)將被迫采用無(wú)類(lèi)型驗(yàn)證系統(tǒng)的C-style cast),并且有很多極不自然甚至丑陋的限制(比如對(duì)線(xiàn)程模型的特殊要求)(參見(jiàn) SCA considered harmful 一文)。與之對(duì)比,如果采用IoC和DSM技術(shù)去實(shí)現(xiàn)同樣的SCA C++組件容器,一個(gè)初級(jí)菜鳥(niǎo)程序員卻能在幾天甚至幾小時(shí)內(nèi)以區(qū)區(qū)數(shù)百行淺顯易懂的代碼輕而易舉地大功告成,且其結(jié)果還遠(yuǎn)勝于IBM老大們嘔心瀝血打造的 參考版(易用,類(lèi)型安全,清除所有不合理限制)(參見(jiàn) SCA as a DSM 一文)。此類(lèi)事半功倍的例子在使用IoC和DSM框架的開(kāi)發(fā)中屢見(jiàn)不鮮。其開(kāi)發(fā)效率一個(gè)數(shù)量級(jí)以上的跳躍改進(jìn)也絕非天方夜譚。70年代關(guān)系數(shù)據(jù)庫(kù)及SQL技術(shù)的引入就使數(shù)據(jù)庫(kù)應(yīng)用的開(kāi)發(fā)效率提高了近兩個(gè)數(shù)量級(jí)。 

            1. IoC
            IoC字面上的意思是“控制反轉(zhuǎn)”(Inversion of Control)。然而其具體含義五花八門(mén)的說(shuō)法卻很容易讓人一頭霧水。這些說(shuō)法往往是過(guò)多地關(guān)注IoC表面的甚至是字面的含義,卻忽略了IoC被用來(lái)解決的實(shí)質(zhì)問(wèn)題(也無(wú)視了這個(gè)概念的歷史和使用現(xiàn)狀)。
            Martin Fowler 就把IoC闡述甚至更名為“依賴(lài)注入”(Dependency Injection )設(shè)計(jì)模式。而IoC鼻祖之一 Stefano Mazzocchi 卻指出Martin Fowler這個(gè)忽悠了一大批人的說(shuō)法實(shí)際上是不得要領(lǐng)(Martin Fowler文章中???碼例子確實(shí)非常誤導(dǎo))。所以,這里并不急于給IoC下一個(gè)教條定義,而是從IoC的實(shí)質(zhì)目的開(kāi)始探討。

            IoC 的概念是Michael Mattson在1996年一篇討論面向?qū)ο罂蚣?Object Oriented Frameworks)的文章中提出的。面向?qū)ο笤O(shè)計(jì)及編程(OOD/OOP)的基本思想簡(jiǎn)單地說(shuō)就是把復(fù)雜軟件系統(tǒng)分解成通過(guò)接口相互合作的對(duì)象。這些 對(duì)象類(lèi)的內(nèi)部實(shí)現(xiàn)之間并不互相牽扯,因而降低了問(wèn)題的復(fù)雜性,且可獨(dú)立靈活地被重用和擴(kuò)展。自然而然,經(jīng)典面向?qū)ο蟮木幊陶Z(yǔ)言(如C++,Java)的側(cè) 重點(diǎn)就是提供語(yǔ)言機(jī)制來(lái)方便并簡(jiǎn)化這種基于對(duì)象類(lèi)的分解,重用和擴(kuò)展。然而,一個(gè)軟件系統(tǒng)的開(kāi)發(fā)效率,可擴(kuò)展性,以及部署維護(hù)升級(jí)的靈活性等并不完全由其 模塊化分解的程度和抽象的優(yōu)劣所決定。很大程度上,將這些分散部件
            有效地組裝成一個(gè)緊密合作的整體更 是決定該軟件項(xiàng)目成???及其產(chǎn)品系統(tǒng)優(yōu)劣的關(guān)鍵。

            以 支持對(duì)象分解為己任的經(jīng)典面向?qū)ο笳Z(yǔ)言(如C++,Java)并沒(méi)有引入超越傳統(tǒng)命令式語(yǔ)言(imperative language)以外的系統(tǒng)組裝部署和配置手段(當(dāng)然,Java 5,C#現(xiàn)在都開(kāi)始往這方面添料)。因而,雖然它們能夠有效地應(yīng)付底層子系統(tǒng)的拼裝和連接,但在進(jìn)行大范圍基于組件(既高層業(yè)務(wù)層模塊)層次的相應(yīng)作業(yè)時(shí) 就顯得簡(jiǎn)陋,死板,冗長(zhǎng)和低效。比如,在使用各種std的IO流類(lèi),STL容器類(lèi)以及boost庫(kù)類(lèi)這些底層模塊類(lèi)時(shí),采用語(yǔ)言本身的機(jī)制(自動(dòng)變量或用 new算符)直截了當(dāng)?shù)貙?shí)例化這些被使用類(lèi)的對(duì)象就盡善盡美了。但在使用高層業(yè)務(wù)模塊時(shí),為了避免對(duì)其具體實(shí)現(xiàn)類(lèi)的依賴(lài),人們不得不疊床架屋對(duì)語(yǔ)言機(jī)制進(jìn) 行額外的手工包裝。應(yīng)運(yùn)而生的是一系列處理所謂管線(xiàn)邏輯(plumbing logic,既非業(yè)務(wù)邏輯)的設(shè)計(jì)模式,比如factory,builder,directory,adaptor,singleton, configuration/property manager,factory的manager甚至manager的manager等等。遺憾的是,一個(gè)世紀(jì)以來(lái)這些實(shí)際上是彌補(bǔ)語(yǔ)言缺陷的權(quán)宜之計(jì)反 而受到狂熱追捧而非深入反思。另外,在傳統(tǒng)軟件的設(shè)計(jì)和實(shí)現(xiàn)中,業(yè)務(wù)邏輯往往直接調(diào)用這些管線(xiàn)邏輯,從而破壞了業(yè)務(wù)邏輯的簡(jiǎn)潔性和獨(dú)立性(比如增加了單元 測(cè)試的困難)。更重要的是,軟件組裝部署和配置的邏輯是支離破碎地散落混跡于各個(gè)業(yè)務(wù)邏輯組件中,既不直觀(往往是見(jiàn)樹(shù)不見(jiàn)林)也不靈活(牽一發(fā)則可能動(dòng) 全身)。往往使得在宏觀結(jié)構(gòu)上理解,維護(hù),修改和擴(kuò)充一個(gè)現(xiàn)有軟件要反而難于當(dāng)初從頭開(kāi)發(fā)這個(gè)軟件。 

            為 解決以上問(wèn)題,Michael Mattson提出了面向?qū)ο罂蚣艿腎oC設(shè)計(jì)原則。依照該原則,管線(xiàn)邏輯被轉(zhuǎn)移并集中至軟件框架內(nèi),業(yè)務(wù)邏輯模塊并不需知道更不必調(diào)用組件框架的服務(wù), 例如不用關(guān)心和調(diào)用其factory或lookup其directory或context等。軟件的組裝部署和配置完全是由管線(xiàn)邏輯框架反過(guò)來(lái)主動(dòng)控制業(yè) 務(wù)邏輯模塊來(lái)安排。Michael Mattson用所謂的好萊塢原則(Hollywood Principle)“別來(lái)電(問(wèn))我,我會(huì)去電(告訴)你”(don't call me, I will call you)形象地比喻了這一設(shè)計(jì)思想。這個(gè)比喻中的“我”指的是負(fù)責(zé)管線(xiàn)邏輯的組件框架,“你”則是被其調(diào)遣配置的一個(gè)組件。比如,在一個(gè)業(yè)務(wù)邏輯模塊A需 要調(diào)用另一個(gè)業(yè)務(wù)邏輯模塊B的場(chǎng)景中,傳統(tǒng)的非IoC的設(shè)計(jì)(比如EJB2.0)是讓A調(diào)用管線(xiàn)邏輯(B的factory或某個(gè)directory服務(wù)) 來(lái)獲得B的引用(或指針)。在IoC框架內(nèi),框架不但完成A和B的實(shí)例化并保持追蹤,而且B的實(shí)例引用(或指針)也是由框架主動(dòng)調(diào)用A的接口函數(shù)(比如構(gòu) 造及賦值函數(shù)等)賦予 A。從而,A的實(shí)現(xiàn)可以專(zhuān)著于業(yè)務(wù)邏輯,而管線(xiàn)細(xì)節(jié)(比如B的實(shí)例化及如何獲得其引用或指針)可以讓外部框架透明地安排妥當(dāng)。這種架構(gòu)完全避免了業(yè)務(wù)邏輯 對(duì)具體管線(xiàn)邏輯框架的牽連從而降低了業(yè)務(wù)邏輯模塊的復(fù)雜度,但更重要的是集中的組裝部署和配置邏輯為提高軟件宏觀結(jié)構(gòu)的直觀性和靈活性鋪平了道路(后面還 將具體討論)。

            簡(jiǎn)短概 括一下,從概念上說(shuō),IoC就是模塊化軟件組裝部署配置框架的一個(gè)設(shè)計(jì)原則。依該原則,業(yè)務(wù)邏輯模塊既不需要處理管線(xiàn)邏輯也可以對(duì)外部管線(xiàn)框架本身一無(wú)所 知(agnostic)。軟件的搭接完全由外部管線(xiàn)框架對(duì)業(yè)務(wù)邏輯模塊的主動(dòng)操控來(lái)完成。從具體實(shí)現(xiàn)上說(shuō),除了一些開(kāi)發(fā)工具以外,IoC框架不過(guò)是一個(gè)封 裝了必備的管線(xiàn)邏輯及IoC機(jī)制的輕型類(lèi)庫(kù)(比如
            PocoCapsule/C++ IoC類(lèi) 庫(kù)大約是70K)。從使用上說(shuō),用戶(hù)制作好業(yè)務(wù)邏輯組件(見(jiàn)下面非侵入性與POCO討論),并將軟件組裝及部署描述(見(jiàn)后面討論)提供給IoC框架(作為 IoC框架庫(kù)函數(shù)的???用參量,或直接驅(qū)動(dòng)一系列庫(kù)函數(shù)調(diào)用)。IoC容器(被調(diào)用的庫(kù)函數(shù))將參照用戶(hù)的描述相應(yīng)地實(shí)例化和配置各個(gè)組件并將它們搭接 為所希望的部署狀態(tài)。 

            2. 非侵入性與POCO
            對(duì) 于象C++和Java這類(lèi)不支持動(dòng)態(tài)類(lèi)型的語(yǔ)言環(huán)境,一個(gè)很自然的問(wèn)題就是業(yè)務(wù)邏輯組件需要支持什么樣的公共接口以使外部IoC框架能對(duì)其進(jìn)行操控。早期 的組件框架(比如Apache Avalon,EJB2.0,CORBA組件模型 CCM,JTRS-SCA等)幾乎清一色地采用侵入式(invasive)設(shè)計(jì),也就是強(qiáng)制規(guī)定業(yè)務(wù)邏輯模塊(稱(chēng)作bean)必須與特定的公共組件接口模 型兼容,既支持由組件框架定義的用來(lái)對(duì)組件進(jìn)行部署配置的公共接口類(lèi)型或基類(lèi)(base class)以及進(jìn)行實(shí)例化的所謂home接口。侵入式設(shè)計(jì)不僅學(xué)習(xí)和使用繁瑣(EJB2.0和CCM均是追求繁瑣,以繁瑣冒充強(qiáng)大的惡例),也大大地限制了組建框架的 開(kāi)放性和適應(yīng)性。因?yàn)楸娍陔y調(diào),所以幾乎每一個(gè)問(wèn)題領(lǐng)域均定義了N個(gè)自己的組件框架和組件接口模型(比如機(jī)器人領(lǐng)域里就至少有10個(gè))。侵入式設(shè)計(jì)導(dǎo)致組 件接口模型只能被其特定的框架所支持,從而形成了各自為政老死不相往來(lái)的組件框架孤島,限制了組件的重用范圍以及框架的通用性。大量這類(lèi)侵入式組件框架以 及與之相應(yīng)的上下左右整個(gè)配套開(kāi)發(fā)體系(如果僥幸有的話(huà))均僅僅是在小范圍內(nèi)被采用,并以高成本在低水平上無(wú)謂地被重復(fù)開(kāi)發(fā)和維護(hù)(CCM就是這方面最?lèi)? 名昭著的例子)。 

            因 此,現(xiàn)代IoC框架大都采用非侵入式(non-invasive)設(shè)計(jì),也就是不對(duì)組件接口模型(即接口及函數(shù)簽名)做任何規(guī)定。換句話(huà)說(shuō),非侵入的 IoC框架一視同仁地支持任何組件接口模型,包括已經(jīng)被定義的和還未被定義的模型,也包括標(biāo)準(zhǔn)組織定義的或用戶(hù)自定義的模型。這些組件接口模型可以采用 (或不采用)任何公共或自定義接口,模板(template)或基類(lèi)(也可以根本不是C++對(duì)象,比如C/C++函數(shù)),可以采用任何(合理的)實(shí)例化或 回收手段和部署配置函數(shù),包括構(gòu)造或析構(gòu)函數(shù),各種自定義 factory/pool/directory lookup函數(shù),以及各種全局或成員函數(shù)等(參見(jiàn)開(kāi)源C++非侵入式IoC框架項(xiàng)目
            PocoCapsule/C++ IoC的介紹 )。在非侵入的C++ IoC組件框架中,因?yàn)樗薪M件無(wú)論其接口模型的新舊美丑高矮胖瘦均被一視同仁地按平頭百姓對(duì)待,故均被統(tǒng)稱(chēng)為“平庸C++對(duì)象”(Plain Old C++ Object )或POCO(相應(yīng)于Java中的“平庸Java對(duì)象”POJO)。

            3. 軟件組裝及部署描述
            在 使用IoC框架的C++軟件開(kāi)發(fā)過(guò)程中,管線(xiàn)模塊及管線(xiàn)銜接代碼轉(zhuǎn)移并集中至IoC框架內(nèi),業(yè)務(wù)邏輯組件(高層模塊或庫(kù)類(lèi))的開(kāi)發(fā)只需關(guān)注業(yè)務(wù)邏輯本身。 軟件的搭接由IoC容器(或庫(kù)類(lèi))按照用戶(hù)提供的軟件組裝及部署描述(assembly and deployment description)完成。因?yàn)橛煞稚⒔M件搭接成的軟件呈樹(shù)狀或更廣義的圖狀結(jié)構(gòu),所以,軟件組裝及部署描述就是對(duì)這種樹(shù)圖結(jié)構(gòu)模型的描述。這種描述 通常有三種表達(dá)(編寫(xiě))形式,既指令代碼(code),元數(shù)據(jù)(metadata),和用戶(hù)數(shù)據(jù)(data)。

            雖 然經(jīng)典面向?qū)ο笳Z(yǔ)言(比如C++和Java)可以有效地按程序步驟構(gòu)造樹(shù)圖狀數(shù)據(jù)結(jié)構(gòu),但對(duì)結(jié)構(gòu)整體的“模型描述”卻蒼白無(wú)力。在C++中,要搭建一個(gè)樹(shù) 圖結(jié)構(gòu)的代碼不外乎就是一步步地去調(diào)用類(lèi)似allocNode(),addNode(),wireNodes()等等函數(shù)。樹(shù)圖結(jié)構(gòu)模型在這種指令式的 (imperative)“步驟描述”中蕩然無(wú)存。對(duì)于管線(xiàn)邏輯比較簡(jiǎn)單的應(yīng)用(例如底層或子模塊),結(jié)構(gòu)模型并不至關(guān)重要甚至是多此一舉,而步驟描述或 干脆拋棄IoC框架也許更直接了當(dāng)。然而對(duì)于管線(xiàn)邏輯較復(fù)雜的應(yīng)用,對(duì)結(jié)構(gòu)搭接具體步驟描述所引入的復(fù)雜性則與使用IoC框架抽象???線(xiàn)邏輯的原始初衷背道 而馳。

            所謂元數(shù)據(jù)表述 就是以編程語(yǔ)言的元數(shù)據(jù)結(jié)構(gòu)來(lái)描述軟件的管線(xiàn)結(jié)構(gòu)模型。其實(shí)質(zhì)就是利用傳統(tǒng)指令性編程語(yǔ)言中類(lèi)(class)結(jié)構(gòu)定義的聲明式語(yǔ)法來(lái)表達(dá)管線(xiàn)結(jié)構(gòu)。比如, 如果一個(gè)結(jié)構(gòu)有10個(gè)節(jié)點(diǎn)(組件),按此方法就是讓用戶(hù)定義一個(gè)包含10個(gè)相應(yīng)成員函數(shù)的類(lèi)(class)并輔助以相應(yīng)注釋?zhuān)╝nnotation)標(biāo)識(shí) 管線(xiàn)連接。進(jìn)行軟件搭接時(shí),IoC框架通過(guò)反射機(jī)制來(lái)解讀這個(gè)類(lèi)的結(jié)構(gòu)并將其看做組件搭接的管線(xiàn)結(jié)構(gòu)描述。這種方法,看似提供了一種聲明式的 (declarative)模型描述,實(shí)際上則屬于一種牽強(qiáng)附會(huì)甚至是生搬硬套的kludge,除了能滿(mǎn)足“只使用編程語(yǔ)言本身來(lái)表述管線(xiàn)邏輯”這一教義 心態(tài)之外不具任何正面意義。

            IoC 框架中有效靈活自然直觀的結(jié)構(gòu)描述形式恰恰就是被大牛們鄙視為惡俗的用戶(hù)文本數(shù)據(jù)描述形式。無(wú)論大牛們對(duì)這種C++和Java語(yǔ)法機(jī)制以外的方法如何深?lèi)? 痛絕,都不得不面對(duì)下面一個(gè)尷尬的窘境。C++和Java這類(lèi)被他們(比如紅帽Jboss的首席科學(xué)家)奉為萬(wàn)能銀彈的編程語(yǔ)言中并不提供對(duì)樹(shù)圖結(jié)構(gòu)整體 具體實(shí)例的有效描述手段。因?yàn)檫@類(lèi)語(yǔ)言的目的僅僅是提供對(duì)象類(lèi)的包裝抽象機(jī)制,而并不是提供具體多對(duì)象系統(tǒng)整體部署結(jié)構(gòu)的模型表述方法(更不要說(shuō)對(duì)各種模 型之間變換,甚至變換的變換的聲明式描述)。

            4. 基于XML的組裝及部署描述
            主 流IoC框架(甚至很多傳統(tǒng)非IoC組件框架如EJB2.0和CCM)中用戶(hù)文本數(shù)據(jù)形式的組裝及部署描述大都是基于XML。XML的設(shè)計(jì)目的恰恰就是提 供對(duì)樹(shù)圖結(jié)構(gòu)的聲明式描述。對(duì)用戶(hù)來(lái)說(shuō),XML標(biāo)準(zhǔn)及技術(shù)成熟穩(wěn)定,已被普遍采用和支持(各種XML解析器和工具滿(mǎn)天飛)。另外,XML框架內(nèi)具備完善的 聲明式結(jié)構(gòu)轉(zhuǎn)換技術(shù)(XSLT,XQuery),為從底層通用IoC組件部署描述提高到“針對(duì)問(wèn)題域的特定建模”DSM(domain specific modeling)準(zhǔn)備???了理想平臺(tái)。最重要的是,與SQL類(lèi)似,XML是種???編程菜鳥(niǎo)都大呼容易的簡(jiǎn)單直觀技術(shù),這就使得很多領(lǐng)域?qū)<夷軌驅(qū)M件構(gòu)成的 系統(tǒng)進(jìn)行搭建和部署。

            下面以開(kāi)源項(xiàng)目
            PocoCapsule/C++ IoC框架 中的一個(gè)具體例子來(lái)介紹這個(gè)方法。這個(gè)例子的完整代碼以及文檔在PocoCapsule源代碼包安裝包 中均可找到,也可以在http://www.pocomatic.com/docs/cpp-examples/basic-ioc/gps 上在線(xiàn)瀏覽。

            這個(gè)例子中所要搭建部署的是一個(gè)如下圖所示包含定時(shí)觸發(fā)器(tick generator),GPS定位器(gps locator),導(dǎo)航???示器(navigate display)三個(gè)組件的GPS系統(tǒng)。

            PocoCapsule容器支持所謂POCO(既前面所說(shuō)的“平庸C++對(duì)象”),從而對(duì)組件接口模型幾乎沒(méi)有任何限制。這三個(gè)組件的基類(lèi)TickGen,GPSLocator和NavDisplay都是由用戶(hù)自己在Interfaces.h 中如下定義的:

            class EventListener {
                public:
            virtual ~EventListener() {}
            virtual void refresh() = 0;
            };

            class EventEmitter {
                public:
            virtual ~EventEmitter() {}
            virtual void subscribe(EventListener*) = 0;
            };

            class TickGen : public EventEmitter
            {
                public:
            virtual void start() = 0;
            };

            class GPSLocator : public EventEmitter, public EventListener
            {
                public:
            virtual int get_pos_x() = 0;
            virtual int get_pos_y() = 0;
            };

            class NavDisplay : public EventListener
            {
                public:
            };

            這些組件的具體實(shí)現(xiàn)類(lèi)TickGenImpl,GPSLocatorImpl和NavDisplayImpl則大致定義如下(忽略所有與組裝不相關(guān)細(xì)節(jié))。

            class TickGenImpl : public TickGen
            {
             ...
                 public:
            TickGenImpl(int cnt, int interval);
            ...
            };

            class GPSLocatorImpl : public GPSLocator
            {
            ...
                public:
            GPSLocatorImpl();
            ...
            };

            class NavDisplayImpl : public NavDisplay {
            ...
                public:
            NavDisplayImpl(GPSLocator* loc) ;
            ...
            };

            這 些組件基類(lèi)和具體實(shí)現(xiàn)類(lèi)的定義以及它們的編譯連接(動(dòng)態(tài)或靜態(tài)庫(kù)均可)等開(kāi)發(fā)制作方式與一般C++應(yīng)用模塊沒(méi)任何區(qū)別,完全不需考慮IoC容器。它們甚至 可以是IoC時(shí)代以前已由第三方制作好,不提供源代碼的模塊。在這個(gè)例子中,除了需要用到這些組件實(shí)現(xiàn)類(lèi)的構(gòu)造算子原型以外省略掉了其他所有不需要關(guān)心的 實(shí)現(xiàn)細(xì)節(jié)。

            接下來(lái),可以用PocoCapsule的XML語(yǔ)法來(lái)描述這個(gè)GPS應(yīng)用。PocoCapsule采用與Spring Framework盡量相近的XML文本格式(schema,定義在
            poco-application.context.dtd 中),針對(duì)C++語(yǔ)言特征進(jìn)行了擴(kuò)充。比如,將bean實(shí)例化后的setter調(diào)用普遍化為任何IoC調(diào)用。在這個(gè)文本格式中, 一個(gè)POCO組件的實(shí)例將被聲明為一個(gè)<bean>元素,包含其構(gòu)造算子的參數(shù)(<method-arg>),實(shí)例化后的IoC (<ioc>),以及IoC方法的參量(也是<method-arg>)等子孫元素。比如,這個(gè)例子中的GPS應(yīng)用結(jié)構(gòu)就可以由 下面一段XML聲明(見(jiàn)setup.xml ):

            ...
            <poco-application-context>
               ...
                <bean class="TickGenImpl" lazy-init="false">
                    <method-arg type="short" value="10"/>
                    <method-arg type="short" value="1"/>
                    <ioc method="subscribe">
                        <method-arg ref="gps-locator"/>
                    </ioc>

                    <ioc method="start"/>
                 </bean>

                <bean id="gps-locator" class="GPSLocatorImpl">
                    <ioc method="subscribe">
                        <method-arg ref="nav-display"/>
                    </ioc>
                </bean>

                <bean id="nav-display" class="NavDisplayImpl">
                    <method-arg ref="gps-locator"/>
                </bean>
            </poco-application-context>

            這段XML聲明簡(jiǎn)單直觀地表達(dá)了三個(gè)組件實(shí)例以及它們之間的互相銜接結(jié)構(gòu)。可以形象化地將它用相應(yīng)的C++形式表示如下:

            TickGenImpl* tick_gen = new TickGenImpl(10, 1);
            tick_gen->subscribe(gps_locator);
            tick_gen->start();

            ...

            GPSLocatorImpl* gps_locator = new GPSLocatorImpl;
            gps_locator->subscribe(nav_display);

            ...

            NavDisplayImpl* nav_display = new NavDisplayImpl(gps_locator);

            雖 然這兩種描述看似表達(dá)同樣的概念,但實(shí)際上他們有本質(zhì)區(qū)別。C++版本表達(dá)的是構(gòu)造這個(gè)應(yīng)用的具體先后步驟,因此實(shí)際上必須重新修改上面C++代碼行的次 序才能讓程序正常編譯和工作。而XML版本表達(dá)的則是一種結(jié)構(gòu),而并非構(gòu)造這個(gè)結(jié)構(gòu)的步驟。結(jié)構(gòu)中各<bean>節(jié)點(diǎn)的實(shí)例化次序與它們?cè)? XML表述中的先后次序無(wú)關(guān),而是由IoC容器根據(jù)用戶(hù)聲明的節(jié)點(diǎn)屬性(比如lazy-init的值)以及銜接時(shí)依賴(lài)關(guān)系的先后來(lái)決定。

            至此,用戶(hù)僅需要將這個(gè)XML描述以文件或字符串形式交給PocoCapsule/C++ IoC容器(既以文件名或XML字符串為參量調(diào)用PocoCapsule/C++ IoC庫(kù)函數(shù),見(jiàn)
            main.C ),讓其自動(dòng)組裝部署所描述???應(yīng)用。關(guān)于PocoCapsule詳細(xì)的使用及工作機(jī)制描述可參閱其入門(mén)教材戶(hù)手冊(cè) ,及代碼實(shí)例

            5. DSM和模型變換
            前 面一節(jié)例子中用到的XML文本格式是由PocoCapsule容器支持的核心格式(core schema)。因?yàn)閄ML在IoC框架中用于描述基于組件應(yīng)用的結(jié)構(gòu)模型,所以這個(gè)格式也稱(chēng)為建模格式。該建模格式與其他IoC容器所采用的XML格式 大同小異。這種格式有直觀易學(xué),格式定義緊湊,普遍適用等優(yōu)點(diǎn)。然而,這些這些具有雙刃劍特征的優(yōu)點(diǎn)也意味著相應(yīng)的缺點(diǎn),諸如直接引用組件編程接口函數(shù)簽 名,表述力和抽象度低,容易造成冗長(zhǎng)的聲明及低級(jí)錯(cuò)誤。比如,前面GPS例子中,XML模型描述直接涉及了有兩個(gè)參量的TickGenImpl的構(gòu)造算 子。從該模型描述中,人們無(wú)從判斷這兩個(gè)參量的目的和意義。如果該模型描述聲明了不匹配的參量類(lèi)型,用戶(hù)可以?xún)e幸地得到IoC容器異常報(bào)告(雖然可能難于 解讀)。但如果這兩個(gè)參量類(lèi)型聲明無(wú)誤,而它們的數(shù)值在無(wú)意間被相互顛倒了(因?yàn)榍『檬峭活?lèi)型),那么用戶(hù)就只能聽(tīng)天由命了。以setter函數(shù)取代多 參量的構(gòu)造或工廠(chǎng)函數(shù)來(lái)完成組件配置看似避免了這一問(wèn)題,但實(shí)際上不但可能破壞無(wú)侵入(non-invasive)原則,而且還可能使模型描述更加冗長(zhǎng)。 同樣,如果試圖增強(qiáng)核心格式以減少這些缺點(diǎn),則會(huì)犧牲其相應(yīng)的優(yōu)點(diǎn)。眾多的重型組件框架(如EJB,CCM等)采用的龐大XML描述格式和令人畏懼的 UML2.0及XMI就是前車(chē)之鑒。

            解 決這個(gè)魚(yú)和熊掌兩難問(wèn)題的一個(gè)有效方法是反其道而行之,干脆不去尋求一個(gè)能青菜蘿卜一刀切“為所有人解決所有問(wèn)題”的建模格式。而是提供一個(gè)???放平 臺(tái),允許用戶(hù)根據(jù)自己的特定需要決定取舍制定最佳建模格式,也就是所謂“針對(duì)問(wèn)題域特定建模”DSM(domain specific modeling)。比如(參見(jiàn)PocoCapsule中
            dsm-gps例子 ),用戶(hù)可以為自己要描述的GPS專(zhuān)門(mén)量身定制一個(gè)DSM格式gps-device.dtd 。按照這個(gè)DSM格式,前面例子中的GPS系統(tǒng)可以被重新描述如下(參見(jiàn)setup.xml ):

            <gps-device>
                <tick-generator use=”TickGenImpl” count=”10” interval=”1”/>
                <gps-locator use=”GPSLocatorImpl”/>
                <navigation-display use=”NavDisplayImpl”/>
            </gps-device>.

            與支持“通用目的建模”GPM(general purpose modeling)的核心格式(poco-application.context.dtd )比較,這個(gè)DSM格式(gps-device.dtd)不但抽象度高,而且定義更緊湊。在其模型描述中完全不涉及組件接口具體函數(shù)簽名,甚至連組件間具體的連接也被藏于幕后。而其配置參數(shù)的含義則一目了然。

            如 前所說(shuō),XML框架中的XSLT和XQuery已經(jīng)為支持這個(gè)DSM格式準(zhǔn)備好了現(xiàn)成的平臺(tái)。這個(gè)平臺(tái)提供了以聲明方式描述模型之間互相轉(zhuǎn)換。因而,用戶(hù) 在定義了一個(gè)DSM后只需要再提供該DSM格式至另一個(gè)建模格式(比如IoC框架的核心建模格式)的轉(zhuǎn)換XSLT描述。那末,集成了XSLT轉(zhuǎn)換器的 IoC框架(比如
            PocoCapsule/C++ IoC and DSM框架 )將會(huì)按照指定的轉(zhuǎn)換描述將一個(gè)由新定義的DSM描述的結(jié)構(gòu)轉(zhuǎn)換為由另一個(gè)DSM定義的結(jié)構(gòu),直至遞歸到該IoC框架的核心格式結(jié)構(gòu)。不僅建模格式可以被 轉(zhuǎn)換,而且模型轉(zhuǎn)換的XSLT描述本身的格式也可以被轉(zhuǎn)換(甚至轉(zhuǎn)換的轉(zhuǎn)換的轉(zhuǎn)換),以簡(jiǎn)化轉(zhuǎn)換描述的設(shè)計(jì)。這就是所謂的“高階轉(zhuǎn)換”HOT(higher order transformation )。關(guān)于IoC框架中DSM模型轉(zhuǎn)換的進(jìn)一步描述可參閱PocoCapsule DSM入門(mén)教材戶(hù)手冊(cè) ,及代碼實(shí)例

            一 個(gè)非侵入(non-invasive)IoC框架一視同仁地支持任何組件接口模型。DSM又使其能輕而易舉地支持任何用戶(hù)或標(biāo)準(zhǔn)組織定義的模型描述格式。 因此,一個(gè)IoC+DSM框架實(shí)際上是一個(gè)非常有效靈活的框架的框架。它讓菜鳥(niǎo)可以輕松且高質(zhì)量地實(shí)現(xiàn)很多組件框架,包括本文開(kāi)始說(shuō)的服務(wù)組件架構(gòu) SCA(參閱),以及軟件無(wú)線(xiàn)電SDR的JTRS-SCA核心框架(CF)組件架構(gòu)(參閱),各種機(jī)器人軟件組件架構(gòu)(參閱),CORBA組件架構(gòu)(參閱),等等如下圖所述的在PocoCapsule中提供的DSM框架。




            posted on 2008-02-01 12:40 kjin101 閱讀(5069) 評(píng)論(14)  編輯 收藏 引用

            評(píng)論

            # re: 頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            強(qiáng)大,但看不明白,什么是管線(xiàn)邏輯框架?什么是組件框架?什么是業(yè)務(wù)邏輯模塊?什么又是業(yè)務(wù)邏輯組件?什么是外部管線(xiàn)?還是算了,我看我還是搞搞語(yǔ)言就算了。
            2008-02-01 15:33 | 汪江濤

            # re: 頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            先頂下 慢慢看
            以前看astrisk的源碼(c語(yǔ)言實(shí)現(xiàn)) 覺(jué)得它的架構(gòu)就完美了 所有模塊都可以動(dòng)態(tài)加載 卸載,所有接口都可以動(dòng)態(tài)注冊(cè) 注銷(xiāo)。
            貌似ace里也有組件配置框架,還沒(méi)研究過(guò)。
            文中的poco的支持 很有吸引力啊
            2008-02-01 15:56 | cppexplore

            # re: 頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            原來(lái)是本blog的開(kāi)山文章啊
            使勁頂啊

            有空來(lái)我blog看看,多多交流!!
            2008-02-01 15:58 | cppexplore

            # re: 頂風(fēng)作案。。。賣(mài)原創(chuàng)狗皮膏藥:閑扯在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            多謝捧場(chǎng)。。。這篇東西在別的地方曾經(jīng)被幾個(gè)老大斥責(zé)為很黃很暴力。。。嘿嘿。。。
            2008-02-02 12:18 | kjin101

            # re: [原創(chuàng)]頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            使勁頂!
            其實(shí)我早就想在C++中使用IoC了,可惜能力有限,怎么都想不到一個(gè)好的實(shí)現(xiàn)方法,眼光有限,也找不到一個(gè)可以使用的IoC框架。
            博主的這篇開(kāi)山之作對(duì)我很有用。

            有空多交流。
            2008-02-08 09:53 | 海邊沫沫

            # re: [原創(chuàng)]頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            冒風(fēng)雨進(jìn)來(lái)看一下,覺(jué)得博主你的句子實(shí)在太長(zhǎng)了點(diǎn):
            如“
            能否有效清晰而又靈活地再將這些相對(duì)獨(dú)立制作的分散部件組裝成一個(gè)緊密合作的整體并完成其部署和配置更是決定該軟件項(xiàng)目成敗及其產(chǎn)品系統(tǒng)優(yōu)劣的關(guān)鍵”
            比英文還難讀啊。
            2008-02-17 19:43 | 驃\

            # re: [原創(chuàng)]頂風(fēng)作案。。。賣(mài)狗皮膏藥:閑扯在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            好象很高深,有空再看。
            2008-02-18 10:41 | 金慶

            # re: 在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            挺有意思的。我也是這么理解ioc的,哪有martin說(shuō)的那么玄乎哦。
            寫(xiě)個(gè)ioc照理不難,可是c++ reflection弱啊。
            我用vcf的reflection框架做過(guò)c++ ioc。
            在自己的c++項(xiàng)目里用ios,感覺(jué)是程序規(guī)模越大,ioc的使程序架構(gòu)清晰的優(yōu)點(diǎn)也越大,不想以前越寫(xiě)越亂。
            現(xiàn)在看上去這個(gè)pococapsule的功能似乎更強(qiáng)。
            語(yǔ)言組織混亂,請(qǐng)諒解
            2008-03-03 23:12 | suntoe

            # re: 在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            是javaeye的suntoe嗎?也是牛人呀。。。以后我可以多同你探討和請(qǐng)教了。個(gè)人感覺(jué)VCF里的reflection太業(yè)余了,居然要求人肉修改poco的頭文件。。。與非侵入思想背道而馳。IoC需要reflect才能實(shí)現(xiàn)才真正有用,這種認(rèn)識(shí)完全是個(gè)誤區(qū)。。。迷惑了很多人。。。事實(shí)上恰恰相反。。。在C++中如果真用reflection(比如CERN的Reflex)來(lái)實(shí)現(xiàn)IoC,所帶來(lái)的問(wèn)題反而比不用reflection多。我E文博客上探討了一下這個(gè)問(wèn)題(<http://ke-jin.blogspot.com/2008/02/dynamic-invocation-proxy-in-pococapsule.html>)。不過(guò)國(guó)內(nèi)有可能上不了內(nèi)個(gè)網(wǎng),過(guò)幾天我會(huì)把該文轉(zhuǎn)到PocoCapsule的wiki上。。。。我說(shuō)話(huà)也是邏輯亂跳。。。估計(jì)編程的都這德行。。。;)
            2008-03-04 08:42 | kjin101

            # re: 在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            看不到http://ke-jin.blogspot.com
            不是很理解dynamic invocation proxies的意思,但pxgenproxy –h=foo.h setup.xml這個(gè)命令行里的foo.h是必須的嗎?是必須的話(huà)那就離不開(kāi)源代碼了,至少是離不開(kāi)頭文件的。這對(duì)于動(dòng)態(tài)裝載進(jìn)來(lái)的、無(wú)法預(yù)知源代碼或頭文件的程序來(lái)說(shuō),譬如說(shuō)那些允許裝載第三方插件的場(chǎng)合,就不太適用了。
            但是reflection卻可以使用在這種場(chǎng)合。譬如說(shuō)在一個(gè)dll/so里,裝載進(jìn)來(lái)時(shí)將reflection信息(class的名字、屬性和方法)注冊(cè)進(jìn)某個(gè)factory之類(lèi)的東西里,然后ioc container就能用class名字在factory里找出并構(gòu)造出那個(gè)class的實(shí)例,這個(gè)過(guò)程不需要頭文件和pxgenproxy這樣的步驟。
            vcf的reflection并不需要修改頭文件,它要求加的那些宏是用來(lái)聲明reflect信息的,完全可以和class分隔開(kāi)放在別的地方的,你對(duì)它的誤解我想可能是因?yàn)樗睦永锇涯切┖陮?xiě)在了class定義里頭。
            老實(shí)說(shuō),可能是受java的影響比較大,我自己并不喜歡pxgenproxy這類(lèi)帶點(diǎn)源代碼生成味道的東西,因?yàn)樗鼤?huì)強(qiáng)迫你必須取得部分源代碼,這不適用于動(dòng)態(tài)的開(kāi)放的基于二進(jìn)制層次的構(gòu)件復(fù)用的環(huán)境。但是,像vcf這類(lèi)的reflection其實(shí)還是依賴(lài)于源代碼,做得還是不徹底,因?yàn)閏++編譯后沒(méi)有meta data了。可是c++ 0x不在乎這個(gè),那我們也沒(méi)轍。
            倒數(shù)第二段話(huà)是:我不是牛人
            最后一句話(huà)是:不是謙虛
            2008-03-10 00:52 | suntoe

            # re: 在C++中使用IoC及DSM框架[未登錄](méi)  回復(fù)  更多評(píng)論   

            > 不是很理解dynamic invocation proxies的意思,但pxgenproxy –h=foo.h > setup.xml這個(gè)命令行里的foo.h是必須的嗎?

            指定foo.h對(duì)pxgenproxy不是必須的,但對(duì)編譯生成后的代碼是必須的。-h=foo.h的目的只不過(guò)是讓pxgenproxy將#include "foo.h"加入到生成代碼中而已。

            > 是必須的話(huà)那就離不開(kāi)源代碼了,至少是離不開(kāi)頭文件的。
            > 這對(duì)于動(dòng)態(tài)裝載進(jìn)來(lái)的、無(wú)法預(yù)知源代碼或頭文件的程序來(lái)說(shuō),
            > 譬如說(shuō)那些允許裝載第三方插件的場(chǎng)合,就不太適用了。

            foo.h只是頭文件,不是源代碼。這里,我們所真正要求的是提供class和函數(shù)的類(lèi)型和簽名聲明。除了debug或腳本程序環(huán)境等少數(shù)特殊工具以外,絕大多數(shù)C++(甚至在ANSI-C)實(shí)際應(yīng)用中并不存在要調(diào)用一個(gè)第三方class或函數(shù)而不知道函數(shù)的簽名或class的聲明的情況。我E文BLOG上討論的就是這個(gè)問(wèn)題。最重要的一點(diǎn)就是:IOC的實(shí)質(zhì)目的并不是提供一個(gè)腳本環(huán)境,而是組件架構(gòu)。你的目的若是需要調(diào)用C++庫(kù)的腳本環(huán)境,請(qǐng)不要考慮POCOCAPSULE。

            > 但是reflection卻可以使用在這種場(chǎng)合。譬如說(shuō)在一個(gè) dll/so里,
            > 裝載進(jìn)來(lái)時(shí)將reflection信息(class的名字、屬性和方法)注冊(cè)進(jìn)某個(gè)
            > factory之類(lèi)的東西里,然后ioc container就能用class名字在factory
            > 里找出并構(gòu)造出那個(gè)class的實(shí)例,這個(gè)過(guò)程不需要頭文件和pxgenproxy
            > 這樣的步驟。

            關(guān)于reflection的陳述我完全同意。但I(xiàn)oC的實(shí)質(zhì)目的(至少對(duì)于PocoCapsule IoC框架來(lái)說(shuō))與reflection并不相同。IoC完全不必照搬其他基于天然reflection機(jī)制的IoC框架實(shí)現(xiàn)。達(dá)到實(shí)質(zhì)目的是最根本的。在我的E文BLOG文中比較了用reflection的缺點(diǎn)。。。。對(duì)絕大多數(shù)C++應(yīng)用來(lái)說(shuō),reflection機(jī)制往往是有害的(主要是增加內(nèi)存占用和限制開(kāi)發(fā)方式。。。比如禁止了模板和宏的應(yīng)用)

            > vcf的reflection并不需要修改頭文件,它要求加的那些宏是用來(lái)聲明reflect
            > 信息的,完全可以和class分隔開(kāi)放在別的地方的,你對(duì)它的誤解我想可能
            > 是因?yàn)樗睦永锇涯切┖陮?xiě)在了class定義里頭。

            謝謝指正。但需要人肉寫(xiě)內(nèi)些宏已經(jīng)與使用IoC減少非業(yè)務(wù)邏輯代碼的編寫(xiě)的除衷相矛盾了。

            > 老實(shí)說(shuō),可能是受java的影響比較大,我自己并不喜歡pxgenproxy
            > 這類(lèi)帶點(diǎn)源代碼生成味道的東西,因?yàn)樗鼤?huì)強(qiáng)迫你必須取得部分源代碼,
            > 這不適用于動(dòng)態(tài)的開(kāi)放的基于二進(jìn)制層次的構(gòu)件復(fù)用的環(huán)境。

            pxgenproxy根本不需要任何源代碼,也根本不會(huì)對(duì)使用第三方二進(jìn)制模塊造成任何障礙。pococapsule實(shí)例代碼(見(jiàn)<http://www.pocomatic.com/docs/cpp-examples)中有很多這些例子。。。比如調(diào)用stdio中的printf(),調(diào)用iostream中的std::cout<<() operator,調(diào)用第三方CORBA或SOAP引擎函數(shù)等等。。。。

            > 但是,像vcf這類(lèi)的reflection其實(shí)還是依賴(lài)于源代碼,
            > 做得還是不徹底,因?yàn)閏++編譯后沒(méi)有 meta data了。
            > 可是c++ 0x不在乎這個(gè),那我們也沒(méi)轍。

            IoC框架的目的不是為了提供debug或腳本環(huán)境,而是為了將管線(xiàn)邏輯分離并轉(zhuǎn)移至框架內(nèi),并將讓框架基于用戶(hù)提供的聲明式描述來(lái)完成管線(xiàn)搭接和應(yīng)用的部署配置。對(duì)于此目的,reflection并不是必須的。相反,在C++中加入reflection將不可避免地增大內(nèi)存開(kāi)銷(xiāo),這對(duì)很多C++應(yīng)用來(lái)說(shuō)是致命的。所以,即使以后的C++升級(jí)加上了對(duì)reflection的支持(甚至CERN現(xiàn)在就有一個(gè)。。。請(qǐng)艘SEAL Reflex),PocoCapsule C++也不會(huì)考慮使用(同樣的道理,PocoCapsule完全避免使用exception, template, rtti, stl, iostream等等機(jī)制和庫(kù),就是要盡量減少內(nèi)存開(kāi)銷(xiāo))。當(dāng)然,PocoCapsule不會(huì)去禁止業(yè)務(wù)邏輯模塊去使用這些機(jī)制或庫(kù)。

            多謝討論!
            2008-03-10 02:51 | kjin101

            # re: 在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            Suntoe, 我把E文博客中相關(guān)的討論轉(zhuǎn)貼過(guò)來(lái)了。。。

            http://www.shnenglu.com/kjin101/archive/2008/03/10/44053.html

            and:

            http://code.google.com/p/pococapsule/wiki/Reflection_from_Projection
            2008-03-10 04:57 | kjin101

            # re: 在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            大概看了一些,不是很懂,也不是很不懂;總之是受教了,謝謝
            2008-03-31 17:01 | 亨德列克

            # re: 在C++中使用IoC及DSM框架  回復(fù)  更多評(píng)論   

            再次閱讀. SCA是忽悠嗎? 需要比較一下PocoCapsule和SCA C++.
            2011-07-07 11:46 | 金慶

            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            婷婷国产天堂久久综合五月| 人妻无码αv中文字幕久久琪琪布| 四虎国产精品成人免费久久| 品成人欧美大片久久国产欧美...| 久久久无码精品亚洲日韩蜜臀浪潮 | 亚洲va久久久噜噜噜久久天堂| 午夜视频久久久久一区| 国产亚洲精午夜久久久久久| 中文字幕一区二区三区久久网站 | 精品久久无码中文字幕| 看久久久久久a级毛片| 综合网日日天干夜夜久久| 2019久久久高清456| 伊人久久综合精品无码AV专区| 久久精品国产精品亚洲精品| 欧美国产成人久久精品| 久久精品国产乱子伦| 亚洲色欲久久久综合网| 久久无码人妻一区二区三区午夜| 欧美噜噜久久久XXX| 精品久久久久香蕉网| 久久亚洲高清观看| 久久精品国产99国产精品| 久久久久久午夜精品| 日日噜噜夜夜狠狠久久丁香五月 | 亚洲国产精品久久久久网站| 99久久国产综合精品成人影院| 国产精品久久久久一区二区三区| 久久国产成人| 久久精品国产亚洲AV不卡| 久久久久人妻一区二区三区vr| 久久久九九有精品国产| 久久av高潮av无码av喷吹| 久久久久亚洲国产| 精品无码久久久久久午夜| 很黄很污的网站久久mimi色| 日本久久中文字幕| 久久久久人妻精品一区| 国产—久久香蕉国产线看观看| 久久久久亚洲AV片无码下载蜜桃| 狠狠色婷婷久久一区二区三区|