<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    codefans

    導(dǎo)航

    <2025年7月>
    293012345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    統(tǒng)計

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    程序設(shè)計鏈接

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    OO隨筆(關(guān)于connection pool系列的補充,兼答bonmot) 選擇自 ajoo 的 Blog

    OO隨筆(關(guān)于connection pool系列的補充,兼答bonmot)

    說起OO, 每個人都有每個人自己的見解。粗淺者如“obj.method的語法就是OO”;高深的則必侃“design pattern”.
    今天我也來說說我的一孔之見。

    什么是OO?
    就是面向接口編程。無論你是用vtable, 或gp的function object, 或就是C的函數(shù)指針,正交分解也好,各種pattern也罷,都是面向接口編程思想的一種實現(xiàn)。

    為什么要面向接口編程?
    為了解耦。

    什么是解耦?
    就是把程序中互相不相關(guān)或有限相關(guān)的模塊分割開來。就象收拾屋子,你希望把不同的東西放到不同的地方。把醬油和醋倒進不同的瓶子里去。
    這里,對完全不相關(guān)的功能,可以簡單地分開實現(xiàn)。
    但事實上,很多情況下,不同模塊之間是有互相之間的關(guān)系的。這時,就需要接口。用接口準確定義模塊之間的關(guān)系。解耦前,兩個模塊之間共享所有信息(這個信息包括數(shù)據(jù),也包括各自的實現(xiàn)細節(jié))。解耦后,需要共享的信息被準確地定義在接口中。同時,信息的流向也被確定。

    解耦的好處是什么呢?
    首先,程序變得清晰了。
    其次,不該暴露的實現(xiàn)細節(jié)被隱藏了。代碼的修改變?nèi)菀琢恕?BR>再次,結(jié)構(gòu)靈活了,通過靜態(tài)多態(tài)(function object)或動態(tài)多態(tài)(vtable), 一個模塊可以和任意實現(xiàn)接口的模塊協(xié)作。原來類A只能與類B協(xié)作,解耦后可以和所有實現(xiàn)接口IB的類如B1, B2, ... 協(xié)作了。擴展性大大增強。自然而然就代碼重用了。
    編譯依賴也沒有了。你可以專心寫和編譯一個模塊,不用等待其它模塊的完成。
    調(diào)試容易了。只要模塊對一個接口調(diào)試成功,其它的接口也沒有問題。于是,甚至可以用一個simple naive的實現(xiàn)該接口的dummy類來調(diào)試。(這點,使用template的gp不適用)

    那么解耦的壞處是什么呢?
    接口的定義變得很關(guān)鍵。解耦就是隱藏一些信息,定義一些需要共享的信息。如果接口定義的不好,隱藏了不該隱藏的信息,那么對某些需要這些信息的復(fù)雜情況來說,這個解耦就失敗了。
    而如果沒有隱藏一些應(yīng)該隱藏的信息,那么不該有的耦合仍然存在。

    那么怎樣解耦,又怎樣定義接口呢?
    這是一個純粹業(yè)務(wù)邏輯的思考過程。這里,對編程語言的知識變得無關(guān)緊要。事實上,只要精確掌握需求,嚴密地分析需求和模塊內(nèi)部子模塊之間的需求,任何一個會邏輯思考的人都可勝任這個工作。就象歌星鄭智化一樣,雖然不識譜,但一樣寫歌,只不過最后要懂譜的人把歌紀錄下來。
    解耦的原則很簡單:精確定義需求,仔細分析需求。不要隱藏任何“需求”也許會需要的信息。不要放過任何“需求”明顯不需要的信息。
    而對需求不清楚的情況,寧可錯放一千,不能錯殺一個。總而言之,決不能隱藏可能需要的信息。
    不考慮重用,重用是解耦后的自然結(jié)果。不能倒因為果!

    至于對這些原則的具體的運用在前面幾篇的connection pool的文章里已經(jīng)有所體現(xiàn)了。


    下面,我先針對bonmot對我的connection pool的例子的疑問進行回答。最后,再對bonmot的一個問題給出我的解決的思路。

     

     

     

    無關(guān)緊要的問題:
    1.ConnectionFactoryImpl也可以繼承方式實現(xiàn)ConnectionFactory

    其實,我最初的實現(xiàn),的確是ConnectoinFactoryImpl implements ConnectionFactory的,
    但后來,當我overload了instance()函數(shù)之后,我發(fā)現(xiàn),這兩個函數(shù)返回的ConnectionFactory的實現(xiàn)類的代碼是不同的。于是,匿名類就誕生了。
    這里,有一點值得吹噓的是,對構(gòu)造函數(shù)的隱藏,使得使用ConnectionFactoryImpl的客戶代碼對我的改動完全不敏感。這也就是我為什么一直鼓吹要隱藏構(gòu)造函數(shù)的原因。
    以下是這個類的實現(xiàn):
    public class ConnectionFactoryImpl
    {
        private ConnectionFactoryImpl(){}
        static public ConnectionFactory instance(final String driver, final String url,
         final String user, final String pwd)
        throws SQLException, ClassNotFoundException{
      final Class driverClass = Class.forName(driver);
      return new ConnectionFactory(){
       private final Class keeper = driverClass;
       public final Connection createConnection()
       throws SQLException{
        return DriverManager.getConnection(url,user,pwd);
       }
      };
        }
        static public ConnectionFactory instance(final String driver, final String url)
        throws SQLException, ClassNotFoundException{
      final Class driverClass = Class.forName(driver);
      return new ConnectionFactory(){
       private final Class keeper = driverClass;
       public final Connection createConnection()
       throws SQLException{
        return DriverManager.getConnection(url);
       }
      };
     } 
    }


    2.ConnectionFactoryImpl中
    private final Class keeper = driverClass;//似乎多余

    是啊,很多代碼里都是禿禿的Class.forName(classname)。也工作的很好。不過,記得在哪篇文章里看到過,在新的java language specification里,動態(tài)加載的類有可能被垃圾回收。如果是這樣,那不麻煩啦?我好容易Class.forName()加載了driver類,好嘛!哪天jvm一高興給我回收啦!所以咱還是以防萬一的好!

    功能的問題
    1.ConnectionPooling是實現(xiàn)pooling的算法,其最基本的就是getConnection(),releaseConnection(Conn)
    為什么不直接在ConnectionPool定義releaseConnection()方法,而要多一個interface ConnectionHome

    首先,我的ConnectionPool接口是直接給用戶使用的。我在該文的第一章就提出,向用戶暴露releaseConnection(Connection)是不好的。你怎樣保證用戶沒有向oracle連接池中返回sql server連接?怎樣保證用戶不會把同一個連接向連接池返回兩次?已經(jīng)有Connection.close(), 用戶為什么要調(diào)用releaseConnection?

    ConnectionHome接口是PooledConnection類定義的。PooledConnection作為一個封裝在物理Connection外的與pool協(xié)同工作的類,它需要知道怎樣返還一個物理Connection. ConnectionHome接口只定義了一個方法:void releaseConnection(Connection), 就是描述這一需求的產(chǎn)物。

    2.事物總是對等的,F(xiàn)actory用于實現(xiàn)物理連接,同樣應(yīng)該負責關(guān)閉物理連接,而不應(yīng)該讓pooling算法關(guān)閉物理連接。另外,獲取與關(guān)閉connection應(yīng)該在一個接口中實現(xiàn),如果分成2個接口,就不能保證連接的實現(xiàn)一定對應(yīng)于關(guān)閉的實現(xiàn)。
    即Factory是物理層,pool是cache層,client是應(yīng)用層。

    首先,ConnectionPooling作為一個描述pooling算法的接口,它需要代表所有可能的pooling算法,所以,我們不能排除在某種pooling算法中,它會以一定的邏輯關(guān)閉物理數(shù)據(jù)庫連接。因此,pooling算法一定要可以在任何時候關(guān)閉這個連接。
    至于是調(diào)用Connection.close(), 還是放一個closeConnection方法在ConnectionFactory中,讓我們先看看一些其它的factory的實現(xiàn)。
    在COM中,IFactory的接口負責生產(chǎn)對象。但釋放對象是由IUnknown::Release()負責的。
    在Java中,很多Factory接口負責生產(chǎn)對象,但垃圾收集負責回收對象。
    為什么這些factory的機制不要求生產(chǎn)者來銷毀對象呢?
    其原因在于類型安全!
    舉個例子:
    class Factory{
       public Object getObject(){
         if(...)return new ClassA();
         else return new ClassB();
       }
       public void release(Object obj){
         if(obj 是ClassA){
             ((ClassA)obj).closeA();
         }
         else{
             ((ClassB)obj).closeB();
         }
         /*丑啊!*/
       }
    }
    在這樣一個工廠里,getObject方法知道生產(chǎn)的對象的真正類型。但在返回之后,該對象的真正類型就被丟失了。
    這樣,如果你再把對象送還給工廠,說:“嘿!這是從你們廠出的,現(xiàn)在我不用了,還給你。”對工廠來說,它需要:
    1, 確認這個對象真是出自本廠。(這可不那么容易)
    2, 確認這個對象是怎么造出來的。以便找出相應(yīng)的銷毀機制(也不容易)
    我們?yōu)槭裁床话裷eleaseConnection對用戶公開?也是因為考慮到用戶可能會錯誤返還非本廠生產(chǎn)的東西。
    其實,當對象出廠之后,只有對象自己才知道怎樣銷毀自己。其它任何對象,包括生產(chǎn)者,都無能為力。


    4.可靠性不夠。表現(xiàn)在:
    a.pool的可靠性應(yīng)該與server的可靠性無關(guān),即database server或socket server可能由于某些原因重新啟動,但pool不應(yīng)該也要重新啟動(比如一個pool存有不同server的connection),否則就跑出錯誤。所以,pool因該檢查物理connection的連接狀態(tài)

    怎么說呢?這屬于ConnectionPool這個接口的語義。我們是否想讓我們的pool即使數(shù)據(jù)庫server崩潰了也能工作呢?
    首先,這樣做是否有意義呢?如果數(shù)據(jù)庫server崩潰了,我們的Connection pool怎么補救呢?
    其次,就算這樣是有意義的,它也是ConnectionPooling的邏輯。完全可以交給一個對此負責的ConnectionPooling處理。

    b.pooled Connection可能由于一個client忘記關(guān)閉,而導(dǎo)致整個pool阻塞。所以,應(yīng)該對pooled Connection進行監(jiān)控,對于超時的或其他invaild狀態(tài)的pooled connection強制回收。

    這個問題提的好!起初,我覺得這也只是另一個ConnectionPooling的邏輯。可以交給一個監(jiān)測已分配的連接使用情況的ConnectionPooling實現(xiàn)來處理。但仔細一想。這樣做是不好的。

    首先,監(jiān)視連接的使用一定會需要在連接對象上記錄一些狀態(tài),象連接分配的時間,最近一次客戶使用該連接的時間等等。而ConnectionPooling的語義是返回pool里的物理連接,而由ConnectionPooling2Pool類來做封裝。這樣,ConnectionPooling的實現(xiàn)就很難紀錄必要的信息。當然,ConnectionPooling也可以在返回物理連接前先做一個wrapper, 把信息紀錄在這個wrapper里。可是,這樣一來,類型安全就得不到保障。在使用該wrapper時,就要進行downcast.

    其次,監(jiān)視已分配連接和管理空閑連接之間到底有多大耦合呢?能否對它們解耦呢?經(jīng)過分析,我感覺,答案是:不能。監(jiān)視已分配連接的算法理論上有可能需要知道空閑連接的一些信息,而反之也是一樣。而且,更討厭的是,它們之間所需要的信息量無法估計,也就是說,對一些特定的算法,它們可能是完全的緊耦合。如果按這樣分析,這種ConnectionPool可能還得要求實現(xiàn)者直接實現(xiàn)ConnectionPool, 就象我們第三章里使用的方法,只能偶爾使用一些utility類,象PooledConnection之類。
    不過,雖然我們不能完全把監(jiān)視算法和分配算法分開。但事實上很多監(jiān)視算法,分配算法確實是互不相關(guān)的。我們也許可以寫一個框架,簡化對這些互不相關(guān)的算法的實現(xiàn)。雖然對完全緊耦合的情況我們無能為力,但對多數(shù)普通的情況,我們還是可以有些作為的。而且,這樣一個框架并不影響對復(fù)雜的緊耦合情況的特殊實現(xiàn)。
    這個框架,當然應(yīng)該和我們現(xiàn)有的框架協(xié)同工作。具體的實現(xiàn)思路,我將在后面給出。

    c.ConnectionPoolingImpl
        public final synchronized void clear(){
          closeAll();
          freeConn.removeAllElements();
        }//沒有transaction保證,有可能引起數(shù)據(jù)不一致,資源(connection)泄漏(connection沒關(guān)閉,pool卻拿掉了)
        可以關(guān)閉一個connection,去掉一個pool對象

    這里不需要transaction保證的。我們先關(guān)閉所有連接,然后再清連接池,怎么可能“connection沒關(guān)閉,pool卻拿掉了”呢?

    擴展的問題
    1.ConnectionPool是否定義成一個結(jié)構(gòu)interface更好,而讓pooling實現(xiàn)pooling算法。
    pool可定義成Vector,Tree,...,負責存儲遍歷,而pooling負責check in,check out.

    數(shù)據(jù)結(jié)構(gòu)和算法永遠是緊耦合的。實際上,算法決定數(shù)據(jù)結(jié)構(gòu),不可能實現(xiàn)定義一個數(shù)據(jù)結(jié)構(gòu),然后強迫所有算法使用。即使是Collection, Iterator之類較抽象的結(jié)構(gòu)也不行。

    2.可能有大型的pool,比如字庫,因此有檢索問題

    這就是ConnectionPooling的實現(xiàn)者要動的腦筋了。我們的框架只定義語義和責任分工,并不牽扯這樣的實現(xiàn)細節(jié)。

    2.更復(fù)雜的是可能每個connection上有多個引用,pooling要負責給client引用最少的那個connection.

    這還是一個實現(xiàn)的細節(jié)。不過我想不出有什么理由我們會要不同客戶共享同一個連接。這是不安全的,不是嗎?

    3.可能同一個pool存儲不同類型的對象,對不同對象的處理是否可用visitor模式。

    還是ConnectionPool的實現(xiàn)者的事。

     


    相關(guān)文章
    對該文的評論
    ajoo ( 2002-08-07)
    myan, 我想我可能是沒有明白你的意思。那個關(guān)于hdc的東西,就當我沒說吧。

    至于pattern與否。我總覺得pattern看看可以。用來和別人交流也不錯。
    但真正做東西時,往往是做完了之后才reverse-engineer, 發(fā)現(xiàn):“啊,我這里用的是bridge嘛,那里用的是state..."
    對于你的gui框架。我想,我會本著“分析需求;用接口定義需求;解耦”的原則來設(shè)計。并且象我上一貼提到的剝皮般地
    進行逐層細分。這樣,即使你最后發(fā)現(xiàn)某一部分不合適,修改也會被局限在最小的范圍內(nèi)。
    舉個例子,它就象通訊協(xié)議里的層:
    定義了網(wǎng)絡(luò)層,下面又可以定義鏈路層,物理層。但對于網(wǎng)絡(luò)層的用戶,其它兩層都是透明的,并不是必然的,或強迫的。如果實現(xiàn)者覺得好,完全可以繞過鏈路層,或再加幾層。無論如何更改,不影響網(wǎng)絡(luò)層的用戶。而遞歸地,對于鏈路層的用戶,物理層的實現(xiàn)又是透明的。

    當然,面向接口的OO, 并不適合對performance敏感到一兩個函數(shù)調(diào)用開銷都要計較的應(yīng)用,aggregate, interface的使用,總會引入一定的開銷。

    bonmot ( 2002-08-05)
    myan,
    個人覺得,設(shè)計時結(jié)構(gòu)性質(zhì)的pattern或不是pattern的框架已經(jīng)成形,refactor時某些實現(xiàn)的pattern會自然而然逐步顯現(xiàn)。

    ajoo,
    如果factory維持對產(chǎn)品的引用,肯定是可以回收的,只是這樣會增加factory的復(fù)雜度,是否好可以商榷。
    ar7_top ( 2002-08-04)
    學(xué)習(xí)
    waveless ( 2002-08-03)
    ajoo關(guān)于DC的那段我沒看懂,myan的意思應(yīng)該是在用到j(luò)peg2dib轉(zhuǎn)換
    的某個函數(shù)里面無法得到和窗口相關(guān)的HDC了。這里的hdc應(yīng)該是個參
    數(shù)。HdbJpeg2Dib里的HDC是那兒來的?

    當然象jpeg2dib這樣的過程中理論上是不應(yīng)該需要HDC做參數(shù)的,但
    是Windows中和DIB有關(guān)的很多函數(shù)都需要HDC。其實只是為了取得一
    些設(shè)置象調(diào)色板什么的,用GetDC(NULL)取個設(shè)備DC給它應(yīng)該就可以。
    不一定非要是某一個窗口的DC。但是這需要先看懂它的代碼,并對
    Windows中有關(guān)DIB的函數(shù)很熟悉。這樣就失去用現(xiàn)成庫的意義了。
    ajoo ( 2002-08-03)
    myan:
    還不是很明白。如果Jpeg2Dib是一個接口:
    interface Jpeg2Dib{
       virtual Dib* convert(Jpeg* jpeg)=0;
    };
    難道你不能這樣實現(xiàn)?
    class HdbJpeg2Dib:public Jpeg2Dib{
       public:
       Dib* convert(Jpeg* jpeg){
          return ::convert_func(hdc, jpeg);
       }
       private:
       const HDC hdc;
    };

    bonmot:
    我覺得你沒有很明白面向接口的意思。為什么不要求保證ConnectionPool實現(xiàn)中一定引用ConnectionPooling接口呢?因為這樣做是不好的。
    面向接口的原則是:誰需要,就有誰定義。提供功能的類可以不定義接口。但需要功能的模塊一定要定義所需要的接口。
    我們的ConnectionPool是pool的用戶需要的。對于一個ConnectionPool的用戶來說,他并不關(guān)心你是用什么巧妙的方法實現(xiàn)的這個ConnectionPool, 只要它能完成所定義的操作,對用戶來說就可以。誰管你是直接實現(xiàn)的還是用的什么pattern? ConnectionPooling相對于ConnectionPool的用戶來說,只是實現(xiàn)細節(jié)。屬于解耦要隱藏的無關(guān)信息。
    所以沒有理由要把ConnectionPooling的細節(jié)公之于眾。

    也許你要說:應(yīng)該強迫程序員在實現(xiàn)ConnectionPool的時候使用ConnectionPooling. 我覺得這種強迫是不應(yīng)該的。無論你的實現(xiàn)方法多么巧妙,你永遠不應(yīng)該把它強加于人。
    而且,現(xiàn)實世界的復(fù)雜性永遠是超過我們的想象的。無論你多么激動于自己天才的實現(xiàn)方法,都不要以為它可以解決所有問題。就象你提出的監(jiān)測已分配的連接的使用,它就完全可能和ConnectionPool的實現(xiàn)緊耦合,而使得我們的bridge pattern (僅僅實現(xiàn)ConnectionPooling)無法有效實現(xiàn)需求。
    另外,即使我們的ConnectionPooling的方法真能解決所有ConnectionPool實現(xiàn)的需求,我們就可以強迫所有實現(xiàn)都用它嗎?如果忽然有人給了我們一個已經(jīng)實現(xiàn)好的connection pool, 它是五年前設(shè)計的,完全沒有用我們的ConnectionPooling的方法,難道我們就不能使用它了嗎?為什么不能簡單地用一個adapter來用它呢?

    其實,我頂煩某些巨大的framework, 你要不就用它,享受它的好處,也忍受它的局限。要不就只能干脆拋開它。象MFC就是這樣。
    一個真正的面向接口的設(shè)計應(yīng)該開放的,我的想象應(yīng)該是這樣:
    首先,假設(shè)需要實現(xiàn)的系統(tǒng)的功能被定義在一系列接口中。ok, 這些接口就是需求,無論你怎樣實現(xiàn)。
    現(xiàn)在,我們要實現(xiàn)這些接口。在實現(xiàn)過程中,我們發(fā)現(xiàn)系統(tǒng)可以被拆成幾個獨立的模塊,(這里的“可以”,可以是我們對需求的分析,認為這幾個模塊可以自然被解耦,也可能是我們發(fā)現(xiàn),雖然該系統(tǒng)理論上是個緊耦合,但對一些特定的場合,還是可以做一些分解以簡化實現(xiàn))。這樣,幾套子需求被定義。系統(tǒng)被拆成幾個小系統(tǒng),幾套子接口,以及組合小系統(tǒng)為大系統(tǒng)的邏輯。
    這里,對頂層接口來說,這些子系統(tǒng),子接口,都不是必須的。它們只是“限于我們有限的知識和經(jīng)驗所使用的一種我們覺得好的實現(xiàn)方法”。所以,如果以后有我們的設(shè)計所沒有預(yù)料到的情況,至少我們還可以直接繞過這層設(shè)計,不會影響頂層的接口。
    好,同樣的分析,分解,遞歸地在實現(xiàn)每個子系統(tǒng)時使用。直到設(shè)計者認為已分解的足夠細,或留到以后refactor時再說。
    在這一層一層的分解過程中,每一層對其上一層來說都不是必須的,并且是透明的。這樣,如果后來的實現(xiàn)者發(fā)現(xiàn)某一個子模塊設(shè)計得不能完全符合需要,都完全可以推倒重來。
    在這樣的一個開放式的設(shè)計中,沒有什么是強迫的。從最頂層的實現(xiàn),到底層的某個小子模塊的實現(xiàn),都不是神圣不可侵犯的。
    當然,系統(tǒng)設(shè)計的目標是要盡量避免將來對相對頂層的子系統(tǒng)設(shè)計更換的可能性。因為,越更換頂層,代價越大,除非,那只是一個用于demo或測試的dummy實現(xiàn), 或一個其它legacy system的adapter。

    這里,設(shè)計者應(yīng)是謙虛的,不是說:“我就是這樣設(shè)計的,想要靈活性?呵呵,我都預(yù)想到了,這里,這里,這里,都是我預(yù)留的供你cusomize的地方。什么?不夠?不可能!”
    而是說:“我的設(shè)計不一定就是最好的,如果你認為有更好的設(shè)計實現(xiàn)方法,或它不能滿足你的需要,你可以輕易地使用你自己的方法。我的任何一層你都可以替換成你的實現(xiàn)。”

    另外,用factory來釋放對象確實是不可行的。用template?template依賴于靜態(tài)類型。而factory方法的返回類型只能是一個。

    elm:
    我覺得你所描述的接口系統(tǒng)就象我說的mfc式的封閉式的系統(tǒng)。繁文縟節(jié)都規(guī)定好了,只能使用,或在實現(xiàn)預(yù)留好的地方做些修改。
    比如說:總經(jīng)理告訴項目經(jīng)理要上一個項目。項目經(jīng)理必須先開動員會,再做項目報告,再做需求分析。。。。。。一系列的過程都定死了。
    而一個開放式的系統(tǒng)是:總經(jīng)理告訴項目經(jīng)理要上一個項目,項目經(jīng)理只負責最終實現(xiàn)這個項目,給出所有需要的deliverables.
    對項目經(jīng)理來說,它可以是用上面描述的方法來組織項目。但那并不是公司要求的。如果需要,完全可以換個項目經(jīng)理,引入一個新的方法。

    myan:
    我倒沒感覺你的那個gui系統(tǒng)就一定是composition或其它的任何一種pattern. :)
    我覺得,用上面說的那種撥皮式的方法,即使你最后要修改實現(xiàn),應(yīng)該也不是個很大的工程。為什么上來就直奔pattern而去呢?
     

    posted on 2005-11-22 11:52 春雷的博客 閱讀(284) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲性一级理论片在线观看| 日本亚洲精品色婷婷在线影院| 国产自偷亚洲精品页65页| 国产成A人亚洲精V品无码性色| 亚洲中文字幕久久精品无码2021| 亚洲αⅴ无码乱码在线观看性色 | 免费少妇a级毛片| 人人狠狠综合久久亚洲婷婷| 一级成人毛片免费观看| 久久不见久久见免费影院| 国产亚洲精品一品区99热| 亚洲AV无码一区二区乱子仑| 日韩精品内射视频免费观看| 四虎影库久免费视频| 亚洲毛片基地4455ww| 夫妻免费无码V看片| 亚洲精品自在线拍| 十八禁在线观看视频播放免费| 国产大片线上免费看| 国产午夜亚洲精品| 中国xxxxx高清免费看视频| 亚洲一区二区三区影院| 色妞www精品视频免费看| 18禁成年无码免费网站无遮挡| 亚洲国产精品成人综合色在线| 国产一区二区三区在线免费观看| 日本中文字幕免费看| 四虎永久成人免费| 韩国免费A级毛片久久| 国产L精品国产亚洲区久久| 亚洲JIZZJIZZ妇女| 亚洲无线一二三四区手机| 男人j进入女人j内部免费网站 | 亚洲天天做日日做天天欢毛片| 一级做a爰片久久毛片免费陪 | 午夜爱爱免费视频| 男女拍拍拍免费视频网站 | 亚洲人成在线播放网站| 免费人成在线观看69式小视频| 亚洲一区二区三区电影| 桃子视频在线观看高清免费视频|