青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天行健 君子當自強而不息

【ZT】敲響OO時代的喪鐘(2)


重用為什么那么難?

程序員都是聰明人,沒有誰愿意干重復勞動這樣的傻事,因此,程序中出現重復代碼是程序員的恥辱。就算不能消除重復代碼,至少也可以對于相同的功能,用不同的代碼來實現所以發明新輪子的程序員才會那么多。

面向對象作為一種橫空出世的新技術,首先承諾的就是“更好的重用性”,而“重用性”這樣一個閃閃發光的詞,也的確能夠吸引程序員的實現,那么多新的理論、新的技術、新的方法、新的框架、新的思想,用來說服別人接受的一個最大的理由,就是“更好的重用性”。然而,OO以及一直以來不斷發展的 OO相關技術,對于重用性的提高,作出了多大的貢獻呢?

JavaEye的age0有一段話特別讓我佩服:“我還是得反復強調,OO設計的價值并不在于所謂的“代碼重用”或者“接口重用”,任何一種設計方法都能夠獲得“重用”的能力,就算是寫匯編一樣可以“重用”。”一個同志能夠如此決絕的對于重用不屑一顧,真是了不起。我們還是來面向大多數希望更好的重用的程序員,分析一下在OO出現之后程序員是如何追求重用這一目標的吧。

在面向過程的時代,重用很簡單,就是代碼的重用,函數、函數庫、模板、模板庫。如此而已。在ADT出現之后,世界分裂了。代碼重用的需求,現在分裂為三個部分:數據類型的重用、ADT內部代碼的重用、操作ADT的代碼的重用。

這句話特別關鍵,讓我再仔細分析給大家看看:ADT=抽象數據類型。就是封裝了操作和數據的一種用戶自定義數據類型。

1、如果僅僅是ADT,那么相近的用戶自定義數據類型就無法重用,因此出現了一個數據類型的重用需求;
2、因為ADT封裝了對于數據的操作,對于A類數據的操作,就無法被B類數據重用,因此出現了一個ADT內部代碼的重用需求;
3、因為對于ADT的操作是借助ADT的外部界面執行的,也就是說,對于接近但是不同的類型的操作,必須寫出不同的代碼,因此出現了對于操作ADT的代碼的重用需求。

這樣的分裂的三個需求,在隨后的OO的發展歷史中,分別被兩種方法來初步滿足。第一、第二種需求,被繼承這樣的技術來試圖滿足;第三種技術被泛型類試圖滿足。這兩個技術滿足了三種重用的需求了嗎?沒有!不但沒有滿足,而且還帶來的諸多麻煩的問題,更在分別滿足三種需求的時候,起了沖突。

由于封裝與重用性之間,存在著本質性的沖突,因此,OO的分析、設計、編程方法就始終處于一個難學、難用、難懂的狀態。我們說給OO下定義非常困難,但是大家都應該承認,ADT是OO的根。數據與操作的封裝是一切OO思想的基礎,也是所有OO信奉者從來沒有懷疑的“前提”!

在繼承與泛型不能解決重用難題之后,OO大師們提出了OO設計原則,提出了OO設計模式,這是我接下來的文章里將要細細批駁的兩大“貢獻”。但是OO的原則、模式,依然沒有解決重用難題。在此之后,又有人提出了AOP、IoC這樣的概念,還有人真正開始和我一樣懷疑封裝的意義,而開發了 CGLib,Mixin這樣的動態改變對象行為與結構的技術,這也是我將要批判的“最新進展”。到了這個時候,真正理解OO本質的人,應該已經看出來了, OO時代即將結束,因OO而帶來的混亂也該結束了。現在唯一的問題是:“什么樣的技術,才是可行的、替代的方案呢?”


OO設計原則批判

OO設計原則,這是很多開發資源網站必備的一個欄目、專題、至少也要轉載一篇放在自己的網站上的東西。所有的程序員,如果你不開發面向對象的程序也就罷了—— 反正你已經落伍很久了,如果你要想開發OO程序,而竟然沒有把那些OO設計原則熟讀背誦,搞得滾瓜爛熟。那么你就完了,一個公司面試你的時候,問你:“你對SRP的理解是怎么樣的?”,而你居然不知道SRP是什么,那么這家公司你也就別想進去了。作為OO程序員的《舊約圣經》(設計模式自然是《新約圣經》)他怎么就會那么神圣呢?

介紹OO設計原則的文章很多,我在google上搜索了一下:“約有58,200項符合OO設計原則的查詢結果”。真正能夠介紹得透徹的,還真是沒幾個。正好我手邊有一本Bob大叔的《UML for JAVA Programmers》那上面的介紹,在我看來,是最好的OO設計原則介紹之一了。另外一本不在手邊的《敏捷軟件開發 原則、模式與實踐》也是Bob大叔的,還要詳盡一些。如果要批判,自然要找這樣的靶子來練!


1、單一職責原則(SRP)

一個類只能因為一個原因而改變。

“A class should have one, and only one, reason to change.”

這個原則何等的簡單,又是何等的模糊呢?什么叫做一個原因呢?我們來看下面這個類:

java代碼:

class User{
    private String name;
    private int age;


    public void setName(String name){
    this.name=name;
}


public void setAge(int age){
    this.age=age;
}
}

請問,這個類是不是違反了SRP原則呢?設置用戶的名字與設置用戶的年齡,是一個原因,還是兩個原因呢?Bob大叔在自己的書里舉了一個例子,說明了違反SRP原則的情況,一個Employee類,包含了計算工資和扣稅金額、在磁盤上讀寫自己、進行XML格式的相互轉換、并且能夠打印自己到各種報表。我說拜托啊大叔!一個類里的方法多到如此驚人的程度,自然是違反了SRP原則,但是我們要為它瘦身,該瘦到什么程度呢?按照大叔繼續給出的自己的答案,它把計算工資和扣稅金額的兩個功能留給了Employee,其他的分離了出去。這個答案正確嗎?員工的工資和稅收是自己算的?還是有一個“財務部”對象來計算的呢?且不說那么掃興的事情,就看看那個類圖里分離出來的那幾個類:

EmployeeXMLConverter、EmployeeDatabase、TaxReport、EmployeeReport、 PayrollReport。這些類還需要有自己的內部數據嗎?請注意,他們事實上都是通過接受Employee對象的內部數據而工作的,換句話說,這些所謂的類,根本就不是什么類,只不過是一個個用Class關鍵字包裹起來的函數庫!當我們看到一個臃腫的Employee類,被拆成6個各不相同的類之后,內心自然升起了“房子打掃干凈之后的喜悅”。但是,且慢!灰塵到哪里去了呢?當我們把一個類拆成6個類之后,那個原本的類自然已經遵守了SRP原則,然后新誕生的5個類,是不是也該遵守SRP原則呢?如果我們不能將一個原則應用于整個系統設計中的所有的對象,僅僅像小孩打掃衛生一樣,把灰塵掃到隔壁房間,這剩下的事情,誰來處理呢?

好吧,我們不要這么嚴厲,畢竟這只是一個原則,追問太深似乎并不合適。我只想再搞清楚幾個問題:按照SRP原則,C++中是不是一律不應該出現多重繼承呢?按照SPR原則,Java中的一個類是不是一律不應該既繼承一個類,又實現一個對象呢?一個簡單的POJO,被動態增強之類的辦法,添加出來的新的持久化能力,是不是也是違反SRP原則的呢?歸根結蒂,我的問題是:按照SPR原則,我那些剩下的,但是又必須要找地方寫的代碼,究竟應該寫在哪里呢?


2、開放-封閉原則(OCP)

軟件實體(類、模塊、方法等)應該允許擴展,不允許修改。

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

這個原則倒是非常的清楚明白,你不能改已經寫好的代碼,而應該擴展已有的代碼!如何做到這一點呢?Bob大叔舉了一個經典的例子:個人認為這個例子說明的是一個使用接口,隔離相互耦合的類的通常做法。而且這個做法不應叫做OCP,而應該叫做DIP。查了一下c2.com里的OCP的解釋:

In other words, (in an ideal world...) you should never need to change existing code or classes: All new functionality can be added by adding new subclasses and overriding methods, or by reusing existing code through delegation.

但是在Bob大叔的OCP解釋中,這個原則的具體實現被偷換了概念,從“鼓勵多使用繼承”,變成了“鼓勵面向接口編程”。為什么?因為繼承式OCP實踐已經被證明會帶來相當多的副作用,而面向接口編程又如何呢?我們在討論DIP的時候再詳細討論吧。

有一個在JavaEye的討論的連接可以參考:對于OCP原則的困惑


3、里斯科夫替換原則(LSP)

子類型必須能夠替代他們的基本類型。

“Subtype must be substitutable for their base types.”

對于這個問題,我都不用多說什么,只引用Bob大叔在c2上的一句話,以作為我的支持。

“I believe that LSP is falsely believed by some few to be a principle of OO design, when really it isn't.”


4、依賴關系倒置原則(DIP)

A.上層模塊應該不依賴于下層模塊,它們都依賴于抽象。

B.抽象不應該依賴于細節,細節應該依賴于抽象。

“A. High level modules should not depend upon low level modules. Both should depend upon abstractions. ”

“B. Abstractions should not depend upon details. Details should depend upon abstractions.”

Bob大叔還補充說明了一下,這個原則的意思就又變了:更確切的說,不要依賴易變的具體類。也就是說,不容易變的具體類,還是可以依賴的。那么,當我們開始一次系統開放的時候,那些類是易變的具體類呢?那些類是不易變的具體類呢?怎么才算是易變、怎么才算是不易變呢?我們來看看代碼吧:

java代碼:

class A{
     public void doA(){
}
}

class B{
     A a=new A();
     a.doA();
}

按照DIP原則,Class B依賴于一個具體實現類Class A,因此是可恥的!最起碼你應該寫成這樣:

java代碼:

interface A{
     public void doA(){
}
}

class AImpl implements A{
     public void doA(){
}
}

class B{
     A a=new AImpl();
     a.doA();
}

這樣,AImpl和B都依賴于Interface A,很漂亮吧。還不夠好,A a=new AImpl();還是很丑陋的,應該進一步隔離!A a=AFactory.createA();在AFactory里,再寫那些見不得人的new AImpl(); 代碼。然后呢?這還沒完,更加Perfect的辦法,是采用XML配置+動態IOC裝配來得到一個A,這樣,B就能夠保證只知道這個世界上有一個 Interface A,而不知道這個Interface A是從哪里來的了。這么做的理由是什么呢?有一個很嚇人的理由:“如果A被B直接使用,那么對于A的任何改動,就會影響到B了。這就叫依賴,而這樣的依賴會很危險!”

我們來看看這頗有說服力的話,漏洞何在?A的變化有兩種情況,一種是只修改A中的方法的內部實現,無論是直接使用A還是使用Interface A的某一個實現,這時候B的代碼都不用改。另一種是修改了方法的調用接口,如果是直接使用A的Class B,就需要修改相關的調用代碼,而如果是使用接口的呢?就需要同時修改Class B和Interface A。這樣看來,采用接口方式,反而需要修改更多的代碼!這使用接口的好處何在?


5、接口隔離原則(ISP)

客戶端不應該依賴于自己不用的方法。

“The dependency of one class to another one should depend on the smallest possible interface.”


這個我就不說了!因為這個原則和SPR原則是矛盾的!就像合成復用原則(CRP)與LSP原則矛盾一樣。

關于這個批判,我昨天晚上只寫了一半,今天算是虎頭蛇尾的寫完了。最后錄一段Bob大叔的話,作為結尾:

什么時候應該運用這些原則呢?首先要給您一個痛苦的告誡,讓所有系統在任何時候都完全遵循所有原則是不明智的。

運用這些原則的最好方式是有的放矢,而不是主動出擊。在您第一次發現代碼中有結構性的問題。或者第一次意識到某個模塊受到另一個模塊的改變的影響時,才應該來看看這些原則中是否有一條或者多條可以用來解決問題。

......

找到得分點的最佳辦法是大量寫單元測試。如果能夠先寫測試,再寫要測試的代碼,效果會更好。

讓我來翻譯一下上面的告誡。原則不是你可以用來預防問題的,而是當你已經陷入麻煩的時候,你可以停下來悔恨一下。至于解決之道,依然不是很清楚,因此,你需要寫大量的單元測試。而且,大量的單元測試并不是幫你檢查你的設計漏洞,而是幫你更真切的感受自己的設計是否正確。至于他究竟是不是正確,這就看個人自己的感覺了。更為驚人的是,在測試驅動開發的建議中,如何驅動開發的準則,竟然是循環的來自于OO設計原則的。

這樣的OO設計原則,就像老爸老媽給我們的人生教誨:“你要做好人啊”,別的什么都沒說。而且我們還遇到了話都說不清的糊涂爹娘,怎么才算好人,不清楚,怎么才算壞人呢?被警察抓了,肯定就是壞人了。至于如何才能做得更好?自己體會吧。


設計模式批判

 為什么要批判設計模式?設計模式不是OO開發的救星嗎?作為“可復用的面向對象”的基礎設施,設計模式大大的超越了OO設計原則給予我們的承諾,還記得我們前面的分析嗎?OO設計原則并不擔保你在設計之前就能避免錯誤,相反的,你往往需要在屢屢受傷之后,才會明白設計原則的真諦。而設計模式是如此的偉大,他甚至可以幫你提前避免問題,只要你可能遇到的問題,符合設計模式手冊中,所描述的那種場景,基本上你就可以直接采用相應的設計方案了。如果找不到正好合適的,你也可以改造自己面對的問題,使得他看起來究就像設計模式手冊中描述的那樣,然后你就可以放心使用相應的設計方案了。如果你無法在那23個模式中找到合適的答案——你可真是太挑剔了——那么你只能自己想法組合一下那23個中的2~3模式,總之,一切盡在其中。

好吧,事實其實沒有那么夸張,“GoF”從來沒有宣稱“設計模式”能夠包治百病,更沒有說過使用“設計模式”可以預防疾病,他們也的確謙虛的承認,設計模式肯定不止23個。但是,GoF也必須承認的一點就是:“Design Patterns原本是用來指導設計的。大多數時候,都是在實際開發之前完成的。”而且,按照設計模式原本的思維模式,應該把一個系統中的各個類,按照他們所說的一堆模式組織起來,其根本目的,就是不讓后來的改動,再去修改以前的代碼,所謂OCP原則在設計模式中的實際體現,就是對擴展開放、對修改封閉。

In other words, (in an ideal world...) you should never need to change existing code or classes: All new functionality can be added by adding new subclasses and overriding methods, or by reusing existing code through delegation.

再強調一遍:“設計模式認為,靈活性、可復用性是設計出來的”,而在此之后的發展我們可以看到,新的大師們又偷換了概念,將“設計——實施”的兩階段過程,變成了一個“TDD+重構”的持續改進過程,他們不但提倡改以有的代碼,而且要大改特改,持續不斷的改,唯一還帶著的帽子是:“重構的目標是得到設計模式。”重構真的能以設計模式為目標嗎?我們下一篇再討論。

請允許我先借力打力,利用重構這一新生事物,攻擊一下設計模式這個老東西。為什么靈活性、可復用性不能是設計出來的?

軟件開發,一個很重要的工作,就是對付“需求變更”,軟件工程的辦法是盡可能的抵擋、或者有效控制變更的發生。技術人員的目標,則是努力開發出更加靈活的技術,以適應可能的變化。值得一提的是,現在軟件開發的管理者,也開始相信,擁抱變化比限制變更,是更為可取的手段了。

更加靈活的技術,更加容易理解,方便描述事實的語言,設計模式等等等等,都是用來適應可能的變化的。對于技術人員來說,如果能夠預測可能的變化,并在系統設計期就將其考慮進去,那么變化就成為計劃的一部分,注意這句話,因為實際的情況往往是:“計劃趕不上變化”。越是自信的技術人員,越是自以為能夠預測可能的變化,將變化提前設計進入自己的系統。所以,濫用設計模式的人,往往是那些自以為水平很高的半桶水。而重構這一思路的出現,就是對于設計模式這種“企圖預測變化”的否定。事實上,即使是重構,也是危險的,因為從原始狀態,到第一個變化發生時,我們能夠得到的只有兩個狀態點,這兩個點聯成直線所指向的一個方向,并不一定就是變化的方向,因此,重構是一個好辦法,而將得到設計模式作為重構的目標,卻相當危險。

設計模式背后的思路非常清楚,就是將可能變化納入設計模式的考慮之中,所以我們看到設計模式的目標“可復用”,其實是一個轉了一個彎以后的目標,“在盡可能重用已有代碼的前提下,適應變化”。我的觀點是:“首先需要滿足的不是復用,而是快速修改”,這個問題太大以后有機會再討論吧。

這篇關于設計模式的批判,我寫了好幾天,始終感覺難以下手。今天和徐昊討論,他的話我認為非常有道理:“設計模式的成功,正好證明了OO的失敗”。這個思路相當有用,我決定就按這個調子來寫。當然,設計模式并不是只有一個,而是有很多很多個,作為一種“專家經驗交流的規范描述格式”,設計模式已經非常多了。我們今天也不批判更多的模式,僅僅對GoF的23個模式下手吧。

GoF的23個設計模式,主要分為三類:創建型模式、結構型模式、行為型模式。我們就分別批判這三種模式吧。

創建型模式之所以需要,其實正好證明了OO的失敗。因為在OO的思想中,創建對象這種事情,天然就應該是對象自己處理的。一個類在定義時,可以定義一個以上的構造函數,來決定如何得到自己的實例,那為什么還需要把創建對象這個事情,交到別人手里?按照SRP原則,無論出于什么原因,創建自己總是自己的職責吧!所以按照SRP原則,所有的創建型模式都不應該出現,出現了也是錯誤的。但是為什么我們需要創建型模式呢?先看看GoF的解釋:“隨著系統演化得越來越依賴于對象復合而不是類繼承,創建型模式變得更為重要。當這種情況發生時,重心從對一組固定行為的硬編碼,轉移為定義一個較小的基本行為集,這些行為可以被組合成任意數目的復雜的行為,這樣創建有特定行為的對象要求的不僅僅是實例化一個類。”

這樣的解釋,有一定的道理,但是卻局限于“用組合取代繼承”這樣一個“當年的熱門話題”。在我看來,根本的原因在于:“在OO的經典思想中,對象之外的環境是不存在的,因此,所有的對象,都要考慮如何產生自己,如何銷毀自己,如何保存自己,總之,自己的事情自己做。”Java的一個巨大進步就在于,銷毀自己這件事情,不再強求對象自己去考慮了,因為這種事情過于瑣碎,而且復雜易錯。但是創建自己這件事情,java依然沒有考慮到也不該交給對象自己考慮的。于是設計模式中的種種創建模式被發明出來,用以滿足復雜多變的創建需求。這個根本原因同時也解釋了為什么單例模式會被發明,按照GoF的解釋,是無法說明為什么我們會需要單例模式地。而當我們有了對象環境的概念之后,各種創建自然有“容器環境”來完成,“單例”模式也只需要在環境中配置,有了OO容器之后,所有的創建模式都被一個概念所代替了。在沒有這樣的概念之前,我們需要用各種模式技巧,來實現“支離破碎”的環境。而在真正的容器環境出現之后,我們就不再需要這些設計模式了。

讓我再說一遍:“如果你能夠理解為什么現在會出現那么多容器,就能理解設計模式中的創建模式,只不過是用來解決OO概念欠缺的一種不完善的手段了。”

再來看結構型模式,個人認為將“Adapter、Bridge、Composite、Decorator、Facade、 Flyweight、Proxy”統統歸入結構型模式,是不恰當的。除了Composite模式算是結構模式,Flyweight算是一種“節約內存的技術手段”之外,其他的模式,都屬于打破OO靜態封裝的技巧。我們知道,按照OO的設定,一個類,就是一種類型,它在代碼寫下的時候,就已經決定了自己的內部數據與外部行為。怎么還能在運行的時候再改變呢?事實證明,這樣需求不但存在,而且重要,設計模式之所以被大家欣賞,一個重要的原因,就是他能夠幫助程序員部分擺脫“靜態封裝屬性與行為”的限制。


OO能從關系型數據庫借鑒些什么?

今天這篇是關于OO VS. RDB的,OO作為一種編程范型,主要是集中于處理“操作”,而RDBMS作為一種數據管理工具,主要是集中于“數據”。但是,在一個需要數據庫的系統中,必然的情況是:操作的對象自然是各種各樣的數據,而數據的管理,自然要通過操作。因此,OO與RDB,從最初淺的角度來理解,雖然分別位于“業務邏輯層”與“數據層”,但是相互之間卻又有著非常緊密的聯系。在OO與RDB之間存在著的緊張關系,其根源在于:“OO世界中的數據是被封裝的孤立數據;RDB世界中的操作是針對行集的。”

因此,一個對象實例內部的數據,應該保存到數據庫中的哪些表、哪些行、哪些列,是一個非常復雜的問題。反過來說,當我們要在內存中恢復一個對象實例時,要從多少表、多少行、多少列中采集數據,并正確轉化為對象實例的內部數據,也是相當的復雜。O/R Mapping,需要考慮的問題還不止于此,在RDB中自然存在的“關系”這一概念,在OO當中,卻沒有明確的對應概念。在OO的世界里,對象之間存在各種各樣的關系,卻非常難以進行規范化的描述。再加上:“添加、修改、刪除、查詢”等等情況的O/R映射,以及與“關系”相關的“級聯操作”——“級聯添加、級聯修改、級聯刪除、級聯查詢”,一個好的O/R Mapping工具,要做出來真是千難萬難。

很多人都意識到了這一點!是的,問題的確存在。但是,為什么呢?該怎么辦呢?幾乎沒有人會反思OO的不是,而是想當然的認為:“關系數據庫技術太老了,跟不上OO技術的進步,因此,我們需要OODB這樣的能夠直接支持OO的數據庫。”

“以其昏昏,使人昭昭”的事情,從來沒有發生過。依著我前面的分析,在OO這樣的基礎薄弱的理論上,怎么可能搞出有實用價值的數據庫呢?

在看到了徐昊的《關于面向對象設計與數據模型的精彩論述》之后,我相信自己找到了知音。他說:“OO在數據模型設計上不具有思維簡潔性。”并且提出了一個重要的詞匯:“邊語義”!這使我相信,和我有類似想法的同志,是存在的。后來又現場聽到了曹曉鋼同志的《ORM時代的數據訪問技術》的演講,并且在他的筆記本里看到了他做的一些代碼,居然與我的很多想法不謀而合!再加上后來與徐昊的幾次MSN交流,終于使我敢于開始寫這樣一篇“OO喪鐘”的文章,因為,我相信自己并不是孤獨的反對者。

OO可以從關系型數據庫那里借鑒些什么呢?

1、關系:也就是徐昊所說的邊語義。在 OO中,對象與對象之間是否存在關系,在對象之外是不知道的。當一個對象被封裝起來以后,他內部是否使用、關聯、組合了其他的對象,是不可知的。因此,我們看到的通常的OO圖,只能說是Object被剖開了以后的對象圖。事實上,關系是被隱藏起來的。而在RDB中,關系非常明確的被定義與標識出來,一目了然。這將帶來巨大的描述效果。相比起UML Class圖,E-R要容易理解得多。

2、Primary Key:這是RDB 特有的概念,在OO中沒有對應概念。因此,我們要判斷兩個對象是否相等,就相當困難。如果每個對象都有一個“一次設置,終身不變的Primary Key”,那么對象之間的比較語義,就能夠被清楚的區分為:IS和LIKE。IS就是Primary Key相同的兩個對象,他們應該完全一致,甚至在內存中,也只應該保存一份。LIKE,就是成員數據相同的兩個對象,他們不是一個東西,僅僅是像而已。

3、SQL:這也是RDB特有的語言,而在OO的世界里,查找一個對象的工作,從來沒有被規范過。
 



posted on 2007-10-01 14:00 lovedday 閱讀(879) 評論(0)  編輯 收藏 引用 所屬分類: ▲ Software Program

公告

導航

統計

常用鏈接

隨筆分類(178)

3D游戲編程相關鏈接

搜索

最新評論

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲校园激情| 亚洲精品男同| 欧美一区二区三区在线免费观看 | 国产精品国产三级国产普通话蜜臀 | 久热精品视频在线观看一区| 亚洲午夜一区二区三区| 欧美激情综合在线| 一区精品久久| 欧美国产日韩在线观看| 欧美日韩成人一区二区三区| 国外视频精品毛片| 久久成人一区二区| 欧美一区二区三区四区在线观看地址 | 欧美永久精品| 国语自产在线不卡| 久久精品亚洲一区二区三区浴池| 亚洲午夜久久久| 国产精品日本| 欧美一级片在线播放| 亚洲欧美日韩一区二区| 国产欧美日韩视频| 久久久夜夜夜| 噜噜噜久久亚洲精品国产品小说| 影音先锋亚洲精品| 亚洲国产精品999| 欧美片网站免费| 亚洲综合色丁香婷婷六月图片| 一区二区三区国产| 国产欧美日韩视频在线观看| 久久香蕉国产线看观看av| 久久免费的精品国产v∧| 亚洲激情视频网站| 一区二区高清在线观看| 国产一区二区| 亚洲激情婷婷| 国产精品自拍网站| 欧美大学生性色视频| 欧美日韩亚洲一区二区| 欧美在线视频a| 欧美va亚洲va日韩∨a综合色| 99精品视频免费全部在线| 日韩视频一区二区在线观看| 国产精品久久看| 免费日韩视频| 欧美日韩理论| 久久综合狠狠综合久久激情| 欧美精品一区二区在线观看| 亚洲欧美综合v| 麻豆成人91精品二区三区| 亚洲欧美另类中文字幕| 久久www成人_看片免费不卡 | 亚洲作爱视频| 黄色av成人| 一区二区精品国产| 亚洲第一页在线| 亚洲天堂av在线免费观看| 原创国产精品91| 国产精品99久久久久久www| 在线观看欧美黄色| 亚洲综合色视频| 99精品国产在热久久下载| 久久精品国产一区二区三| 亚洲一区二区视频| 老司机成人网| 久久精品毛片| 国产精品国产成人国产三级| 欧美不卡在线视频| 国产一区在线观看视频| 亚洲午夜精品国产| 久久久精品一品道一区| 伊人激情综合| 亚洲欧美国产精品桃花| 亚洲毛片av在线| 久久嫩草精品久久久精品| 午夜视频久久久| 欧美日韩精品二区第二页| 欧美1级日本1级| 国产一级揄自揄精品视频| 中日韩在线视频| 在线一区亚洲| 欧美日韩国产不卡| 亚洲国产精品va在线观看黑人 | 亚洲视频精选| 欧美激情综合五月色丁香| 欧美成人综合网站| 国语自产精品视频在线看8查询8 | 国产精品久久久久久久免费软件| 亚洲黄一区二区| 最新中文字幕亚洲| 久久亚洲综合色| 免费成人美女女| 亚洲国产成人av好男人在线观看| 久久国产精品电影| 久久激情一区| 国产日韩一区二区三区在线| 一本色道久久88综合日韩精品| 一区二区黄色| 欧美视频一区二| 亚洲欧洲另类| 欧美.com| 亚洲美女在线一区| 亚洲午夜高清视频| 国产精品成av人在线视午夜片| 亚洲天堂av综合网| 久久久久久久一区二区三区| 久久www成人_看片免费不卡| 久久综合电影| 亚洲精品影院在线观看| 欧美日韩美女| 亚洲性视频h| 久久精品国产一区二区三区| 在线观看精品一区| 欧美噜噜久久久xxx| 夜夜嗨av一区二区三区中文字幕| 亚洲手机成人高清视频| 国产精品一区2区| 久久久免费精品视频| 亚洲日产国产精品| 午夜电影亚洲| 在线精品福利| 欧美日韩综合精品| 久久国产精品网站| 亚洲国产经典视频| 欧美一区成人| 亚洲国产精品久久久久婷婷老年| 欧美福利一区二区三区| 这里只有精品电影| 国产亚洲美州欧州综合国| 欧美在线免费观看亚洲| 亚洲国产成人av好男人在线观看| 日韩一级在线| 国产精品免费网站| 麻豆精品在线播放| 日韩亚洲欧美精品| 国产日韩精品一区观看| 99视频精品免费观看| 伊人久久久大香线蕉综合直播| 欧美大胆a视频| 亚洲欧美视频在线| 91久久精品国产91久久性色| 亚洲欧美精品suv| 91久久国产综合久久| 国产乱码精品1区2区3区| 免费观看不卡av| 欧美一区二区高清| 日韩西西人体444www| 麻豆精品在线视频| 性刺激综合网| 在线亚洲成人| 亚洲国产精品第一区二区| 国产乱子伦一区二区三区国色天香 | 亚洲性视频h| 亚洲黄色一区| 久久久精品一区| 日韩一级片网址| 亚洲国产天堂久久综合网| 国产日韩一区二区三区| 国产精品成人国产乱一区| 欧美激情在线狂野欧美精品| 久久亚洲综合色| 久久riav二区三区| 新67194成人永久网站| 亚洲午夜在线| 亚洲欧洲精品一区二区| 久久精品女人| 欧美一区亚洲一区| 亚洲欧美日本国产专区一区| 一本高清dvd不卡在线观看| 亚洲国产成人av| 亚洲国产美女| 亚洲人成人77777线观看| 亚洲第一精品久久忘忧草社区| 国产亚洲欧美日韩日本| 国产精品素人视频| 国产精品一区二区三区四区五区 | 亚洲国产精品精华液2区45 | 久久久久久亚洲精品中文字幕| 午夜精品区一区二区三| 亚洲欧美国内爽妇网| 午夜精品国产| 欧美一区二区三区四区在线| 亚洲欧美在线看| 午夜久久久久久久久久一区二区| 亚洲午夜成aⅴ人片| 亚洲欧美亚洲| 久久9热精品视频| 久久青青草综合| 老司机午夜免费精品视频| 欧美freesex交免费视频| 欧美r片在线| 亚洲国产女人aaa毛片在线| 亚洲精品老司机| 亚洲视频一区| 欧美在线视频观看免费网站| 久久天堂av综合合色| 免费人成网站在线观看欧美高清 | 亚洲一区在线免费观看| 亚洲愉拍自拍另类高清精品| 欧美永久精品| 欧美国产日本高清在线|