成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊Clean Code的編碼、重構(gòu)技巧

開發(fā) 后端
本文主要針對面向?qū)ο缶幊痰腸lean code來闡述,面向過程代碼的思路會比較不同,不在本文的討論范疇。

clean code,顧名思義就是整潔的代碼,或者說清晰、漂亮的代碼,相信大多數(shù)工程師都希望自己能寫出這樣的代碼。

也許這是個千人千面的話題,每個工程師都有自己的理解。比如我,從一個天天被罵代碼寫得爛的人,逐漸學(xué)習(xí)成長,到現(xiàn)在也能寫的出“人模人樣”的代碼來了。這期間算是積累了一點經(jīng)驗心得,想和大家分享,拋磚引玉。

本文主要針對面向?qū)ο缶幊痰腸lean code來闡述,面向過程代碼的思路會比較不同,不在本文的討論范疇。

代碼整潔的大前提

代碼大部分時候是用來維護(hù)的,而不是用來實現(xiàn)功能的

這個原則適用于大部分的工程。我們的代碼,一方面是編譯好讓機(jī)器執(zhí)行,完成功能需求;另一方面,是寫給身邊的隊友和自己看的,需要長期維護(hù),而且大部分項目都不是朝生夕死的短命鬼。

大部分情況下,如果不能寫出清晰好看的代碼,可能自己一時爽快,后續(xù)維護(hù)付出的代價和成本將遠(yuǎn)高于你的想象。

對清晰好看代碼的追求精神,比所有的技巧都要重要。

優(yōu)秀的代碼大部分是可以自描述的,好于文檔和注釋

當(dāng)你翻看很多開源代碼時,會發(fā)現(xiàn)注釋甚至比我們自己寫的項目都少,但是卻能看的很舒服。當(dāng)讀完源碼時,很多功能設(shè)計就都清晰明了了。通過仔細(xì)斟酌的方法命名、清晰的流程控制,代碼本身就可以拿出來當(dāng)作文檔使用,而且它永遠(yuǎn)不會過期。

相反,注釋不能讓寫的爛的代碼變的更好。如果別人只能依靠注釋讀懂你的代碼的時候,你一定要反思代碼出現(xiàn)了什么問題(當(dāng)然,這里不是說大家不要寫注釋了)。

說下比較適合寫注釋的兩種場景:

  1. public interface,向別人明確發(fā)布你功能的語義,輸入輸出,且不需要關(guān)注實現(xiàn)。
  2. 功能容易有歧義的點,或者涉及比較深層專業(yè)知識的時候。比如,如果你寫一個客戶端,各種config參數(shù)的含義等。

設(shè)計模式只是手段,代碼清晰才是目的

之前見過一些所謂“高手”的代碼都比較抽象,各種工廠、各種繼承。想找到一個實現(xiàn)總是要山路十八彎,一個工程里大部分的類是抽象類或者接口,找不到一兩句實現(xiàn)的代碼,整個讀起代碼來很不順暢。我跟他聊起來的時候,他的主要立場是:保留合適的擴(kuò)展點,克服掉所有的硬編碼。

其實在我看來,也許他的代碼被“過度設(shè)計”了。首先必須要承認(rèn)的是,在同一個公司工作的同事,水平是參差不齊的。無論你用了如何高大上的設(shè)計,如果大多數(shù)人都不能理解你的代碼或者讀起來很費勁的話,其實這是一個失敗的設(shè)計。

當(dāng)你的系統(tǒng)內(nèi)大部分抽象只有一個實現(xiàn)的時候,要好好思考一下,是不是設(shè)計有點過度了,清晰永遠(yuǎn)是***準(zhǔn)則。

代碼整潔的常見手段

記住原則后,我們開始進(jìn)入實踐環(huán)節(jié),先來看下有哪些促成clean code的常見手段。

code review

很多大公司會用git的pull request機(jī)制來做code review。我們重點應(yīng)該review什么?是代碼的格式、業(yè)務(wù)邏輯還是代碼風(fēng)格?我想說的是,凡是能通過機(jī)器檢查出來的事情,無需通過人。比如換行、注釋、方法長度、代碼重復(fù)等。除了基本功能需求的邏輯合理沒有bug外,我們更應(yīng)該關(guān)注代碼的設(shè)計與風(fēng)格。比如,一段功能是不是應(yīng)該屬于一個類、是不是有很多相似的功能可以抽取出來復(fù)用、代碼太過冗長難懂等等。

我個人非常推崇集體code review,因為很多時候,組里相對高級的工程師能夠一眼發(fā)現(xiàn)代碼存在較大設(shè)計缺陷,提出改進(jìn)意見或者重構(gòu)方式。我們可以在整個小組內(nèi)形成一個好的文化傳承和風(fēng)格統(tǒng)一,并且很大程度上培養(yǎng)了大家對clean code的熱情。

勤于重構(gòu)

好的代碼,一般都不是一撮而就的。即使一開始設(shè)計的代碼非常優(yōu)秀,隨著業(yè)務(wù)的快速迭代,也可能被改的面目全非。

為了避免重構(gòu)帶來的負(fù)面影響(delay需求或者帶來bug),我們需要做好以下的功課:
① 掌握一些常見的“無痛”重構(gòu)技巧,這在下文會有具體講解。
② 小步快跑,不要企圖一口吃成個胖子。改一點,測試一點,一方面減少代碼merge的痛苦,另一方面減少上線的風(fēng)險。
③ 建立自動化測試機(jī)制,要做到即使代碼改壞了,也能保證系統(tǒng)最小核心功能的可用,并且保證自己修改的部分被測試覆蓋到。
④ 熟練掌握IDE的自動重構(gòu)功能。這些會很大程度上減少我們的體力勞動,避免犯錯。

靜態(tài)檢查

現(xiàn)在市面上有很多代碼靜態(tài)檢查的工具,也是發(fā)現(xiàn)bug和風(fēng)格不好的比較容易的方式。可以與發(fā)布系統(tǒng)做集成,強(qiáng)制把主要問題修復(fù)掉才可以上線。目前美團(tuán)點評技術(shù)團(tuán)隊內(nèi)部的研發(fā)流程中已經(jīng)普遍接入了Sonar質(zhì)量管理平臺。

多讀開源代碼和身邊優(yōu)秀同學(xué)的代碼

感謝開源社區(qū),為我們提供了這么好的學(xué)習(xí)機(jī)會。無論是JDK的源碼,還是經(jīng)典的Netty、Spring、Jetty,還是一些小工具如Guava等,都是clean code的典范。多多學(xué)習(xí),多多反思和總結(jié),必有收益。

代碼整潔的常見技巧

前面的內(nèi)容都屬于熱身,讓大家有個整體宏觀的認(rèn)識。下面終于進(jìn)入干貨環(huán)節(jié)了,我會分幾個角度講解編寫整潔代碼的常見技巧和誤區(qū)。

通用技巧

單一職責(zé)

這是整潔代碼的最重要也是最基本的原則了。簡單來講,大到一個module、一個package,小到一個class、一個method乃至一個屬性,都應(yīng)該承載一個明確的職責(zé)。要定義的東西,如果不能用一句話描述清楚職責(zé),就把它拆掉。

我們平時寫代碼時,最容易犯的錯誤是:一個方法干了好幾件事或者一個類承載了許多功能。

先來聊聊方法的問題。個人非常主張把方法拆細(xì),這是復(fù)用的基礎(chǔ)。如果方法干了兩件事情,很有可能其中一個功能的其他業(yè)務(wù)有差別就不好重用了。另外語義也是不明確的。經(jīng)常看到一個get()方法里面竟然修改了數(shù)據(jù),這讓使用你方法的人情何以堪?如果不點進(jìn)去看看實現(xiàn),可能就讓程序陷入bug,讓測試陷入麻煩。

再來聊聊類的問題。我們經(jīng)常會看到“又臭又長”的service/biz層的代碼,里面有幾十個方法,干什么的都有:既有增刪改查,又有業(yè)務(wù)邏輯的聚合。每次找到一個方法都費勁。不屬于一個領(lǐng)域或者一個層次的功能,就不要放到一起。

我們team在code review中,最常被批評的問題,就是一個方法應(yīng)該歸屬于哪個類。

優(yōu)先定義整體框架

我寫代碼的時候,比較喜歡先去定義整體的框架,就是寫很多空實現(xiàn),來把整體的業(yè)務(wù)流程穿起來。良好的方法簽名,用入?yún)⒑统鰠砜刂屏鞒獭_@樣能夠避免陷入業(yè)務(wù)細(xì)節(jié)無法自拔。在腦海中先定義清楚流程的幾個階段,并為每個階段找到合適的方法/類歸屬。

這樣做的好處是,閱讀你代碼的人,無論讀到什么深度,都可以清晰地了解每一層的職能,如果不care下一層的實現(xiàn),完全可以跳過不看,并且方法的粒度也會恰到好處。

簡而言之,我比較推崇寫代碼的時候“廣度優(yōu)先”而不是“深度優(yōu)先”,這和我讀代碼的方式是一致的。當(dāng)然,這件事情跟個人的思維習(xí)慣有一定的關(guān)系,可能對抽象思維能力要求會更高一些。如果開始寫代碼的時候這些不夠清晰,起碼要通過不斷地重構(gòu),使代碼達(dá)到這樣的成色。

清晰的命名

老生常談的話題,這里不展開講了,但是必須要mark一下。有的時候,我思考一個方法命名的時間,比寫一段代碼的時間還長。原因還是那個邏輯:每當(dāng)你寫出一個類似于"temp"、"a"、"b"這樣變量的時候,后面每一個維護(hù)代碼的人,都需要用幾倍的精力才能理順。

并且這也是代碼自描述最重要的基礎(chǔ)。

避免過長參數(shù)

如果一個方法的參數(shù)長度超過4個,就需要警惕了。一方面,沒有人能夠記得清楚這些函數(shù)的語義;另一方面,代碼的可讀性會很差;***,如果參數(shù)非常多,意味著一定有很多參數(shù),在很多場景下,是沒有用的,我們只能構(gòu)造默認(rèn)值的方式來傳遞。

解決這個問題的方法很簡單,一般情況下我們會構(gòu)造paramObject。用一個struct或者一個class來承載數(shù)據(jù),一般這種對象是value object,不可變對象。這樣,能極大程度提高代碼的可復(fù)用性和可讀性。在必要的時候,提供合適的build方法,來簡化上層代碼的開發(fā)成本。

避免過長方法和類

一個類或者方法過長的時候,讀者總是很崩潰的。簡單地把方法、類和職責(zé)拆細(xì),往往會有立竿見影的成效。以類為例,拆分的維度有很多,常見的是橫向/縱向。例如,如果一個service,處理的是跟一個庫表對象相關(guān)的所有邏輯,橫向拆分就是根據(jù)業(yè)務(wù),把建立/更新/修改/通知等邏輯拆到不同的類里去;而縱向拆分,指的是
把數(shù)據(jù)庫操作/MQ操作/Cache操作/對象校驗等,拆到不同的對象里去,讓主流程盡量簡單可控,讓同一個類,表達(dá)盡量同一個維度的東西。

讓相同長度的代碼段表示相同粒度的邏輯

這里想表達(dá)的是,盡量多地去抽取private方法,讓代碼具有自描述的能力。舉個簡單的例子

  1. class DoMerger{ 
  2.    map params1; 
  3.    map params2; 
  4.    Do1 do1; 
  5.    Do2 do2; 
  6.    public DoMerger(Map params1,Map params2){ 
  7.       this.params1 = params1; 
  8.       this.params2 = parmas2; 
  9.    } 
  10.    public void invoke(){ 
  11.        do1 = getDo1(); 
  12.        do2 = getDo2(); 
  13.       mergeDO(do1,do2); 
  14.    } 
  15.    private Do1 getDo1(); 
  16.     private Do2 getDo2(); 
  17.     private void mergeDo(){ 
  18.        print(do1+do2); 
  19.     } 

類似這種代碼,在業(yè)務(wù)代碼中隨處可見。獲取do1是一個方法,merge是一個方法,但獲取do2的代碼卻在主流程里寫了。這種代碼,流程越長,讀起來越累。很多人讀代碼的邏輯,是“廣度優(yōu)先”的。先讀懂主流程,再去看細(xì)節(jié)。類似這種代碼,如果能夠把構(gòu)造do2的代碼,提取一個private 方法,就會舒服很多。

面向?qū)ο笤O(shè)計技巧

貧血與領(lǐng)域驅(qū)動

不得不承認(rèn),Spring已經(jīng)成為企業(yè)級Java開發(fā)的事實標(biāo)準(zhǔn)。而大部分公司采用的三層/四層貧血模型,已經(jīng)讓我們的編碼習(xí)慣,變成了面向DAO而不是面向?qū)ο蟆?/p>

缺少了必要的模型抽象和設(shè)計環(huán)節(jié),使得代碼冗長,復(fù)用程度比較差。每次擼代碼的時候,從mapper擼起,好像已經(jīng)成為不成文的規(guī)范。

好處是上手簡單,學(xué)習(xí)成本低。但是每次都不能重用,然后面對兩三千行的類看著眼花的時候,我的心是很痛的。關(guān)于領(lǐng)域驅(qū)動的設(shè)計模式,本文不會展開去講。回歸面向?qū)ο螅€是跟大家share一些比較好的code技巧,能夠在一個通用的框架下,盡量好的寫出漂亮可重用的code。

個人認(rèn)為,一個好的系統(tǒng),一定離不開一套好的模型定義。梳理清楚系統(tǒng)中的核心模型,清楚的定義每個方法的類歸屬,無論對于代碼的可讀性、可交流性,還是和產(chǎn)品的溝通,都是有莫大好處的。

為每個方法找到合適的類歸屬,數(shù)據(jù)和行為盡量要在一起

如果一個類的所有方法,都是在操作另一個類的對象。這時候就要仔細(xì)想一想類的設(shè)計是否合理了。理論上講,面向?qū)ο蟮脑O(shè)計,主張數(shù)據(jù)和行為在一起。這樣,對象之間的結(jié)構(gòu)才是清晰的,也能減少很多不必要的參數(shù)傳遞。

不過這里面有一個要討論的方法:service對象。如果操作一個對象數(shù)據(jù)的所有方法都建立在對象內(nèi)部,可能使對象承載了很多并不屬于它本身職能的方法。

例如,我定義一個類,叫做person,。這個類有很多行為,比如:吃飯、睡覺、上廁所、生孩子;也有很多字段,比如:姓名、年齡、性格。

很明顯,字段從更大程度上來講,是定義和描述我這個人的,但很多行為和我的字段并不相關(guān)。上廁所的時候是不會關(guān)心我是幾歲的。如果把所有關(guān)于人的行為全部在person內(nèi)部承載,這個類一定會膨脹的不行。

這時候就體現(xiàn)了service方法的價值,如果一個行為,無法明確屬于哪個領(lǐng)域?qū)ο螅瑺繌?qiáng)地融入領(lǐng)域?qū)ο罄铮瑫@得很不自然。這時候,無狀態(tài)的service可以發(fā)揮出它的作用。但一定要把握好這個度,回歸本質(zhì),我們要把屬于每個模型的行為合理的去劃定歸屬。

警惕static

static方法,本質(zhì)上來講是面向過程的,無法清晰地反饋對象之間的關(guān)系。雖然有一些代碼實例(自己實現(xiàn)單例或者Spring托管等)的無狀態(tài)方法可以用static來表示,但這種抽象是淺層次的。說白了,如果我們所有調(diào)用static的地方,都寫上import static,那么所有的功能就由類自己在承載了。

讓我畫一個類圖?尷尬了……畫不出來。

而單例的膨脹,很大程度上也是貧血模型帶來的副作用。如果對象本身有血有肉,就不需要這么多無狀態(tài)方法。

static真正適用的場景:工具方法,而不是業(yè)務(wù)方法。

巧用method object

method object是大型重構(gòu)的常用技巧。當(dāng)一段邏輯特別復(fù)雜的代碼,充斥著各種參數(shù)傳遞和是非因果判斷的時候,我首先想到的重構(gòu)手段是提取method object。所謂method object,是一個有數(shù)據(jù)有行為的對象。依賴的數(shù)據(jù)會成為這個對象的變量,所有的行為會成為這個對象的內(nèi)部方法。利用成員變量代替參數(shù)傳遞,會讓代碼簡潔清爽很多。并且,把一段過程式的代碼轉(zhuǎn)換成對象代碼,為很多面向?qū)ο缶幊滩趴梢允褂玫睦^承/封裝/多態(tài)等提供了基礎(chǔ)。

舉個例子,上文引用的代碼如果用method object表示大概會變成這樣

 class DoMerger{     map params1;     map params2;     Do1 do1;     Do2 do2;     public DoMerger(Map params1,Map params2){        this.params1 = params1;        this.params2 = parmas2;     }     public void invoke(){         do1 = getDo1();         do2 = getDo2();        mergeDO(do1,do2);     }     private Do1 getDo1();      private Do2 getDo2();      private void mergeDo(){         print(do1+do2);      }  } 

面向接口編程

面向接口編程是很多年來大家形成的共識和***實踐。最早的理論是便于實現(xiàn)的替換,但現(xiàn)在更顯而易見的好處是避免public方法的膨脹。一個對外publish的接口,一定有明確的職責(zé)。要判斷每一個public方法是否應(yīng)該屬于同一個interface,是很容易的。

整個代碼基于接口去組織,會很自然地變得非常清晰易讀。關(guān)注實現(xiàn)的人才去看實現(xiàn),不是嘛?

正確使用繼承和組合

這也是個在業(yè)界被討論過很久的問題,也有很多論調(diào)。***的觀點是組合的使用一般情況下比繼承更為靈活,尤其是單繼承的體系里,所以傾向于使用組合
,否則會讓子類承載很多不屬于自己的職能。

個人對此觀點持保留意見,在我經(jīng)歷過的代碼中,有一個小規(guī)律,我分析一下。

protected abstract 這種是最值得使用繼承的,父類保留擴(kuò)展點,子類擴(kuò)展,沒什么好說的。

protected final 這種方法,子類是只能使用不能修改實現(xiàn)的。一般有兩種情況:
① 抽象出主流程不能被修改的,然而一般情況下,public final更適合這個職能。如果只是流程的一部分,需要思考這個流程的類歸屬,大部分變成public組合到其他類里是更合適的。
② 父類是抽象類無法直接對外提供服務(wù),又不希望子類修改它的行為,這種大多數(shù)情況下屬于工具方法,比較適合用另一個領(lǐng)域?qū)ο髞沓休d并用組合的方式來使用。

protected 這種是有爭議的,是父類有默認(rèn)實現(xiàn)但子類可以擴(kuò)展的。凡是有擴(kuò)展可能的,使用繼承更理想一些。否則,定義成final并考慮成組合。

綜上所述,個人認(rèn)為繼承更多的是為擴(kuò)展提供便利,為復(fù)用而存在的方法***使用組合的方式。當(dāng)然,更為大的原則是明確每個方法的領(lǐng)域劃分。

代碼復(fù)用技巧

模板方法

這是我用得最多的設(shè)計模式了。每當(dāng)有兩個行為類似但又不完全相同的代碼段時,我總是會想到模板方法。提取公共流程和可復(fù)用的方法到父類,保留不同的地方作為abstract方法,由不同的子類去實現(xiàn)。

并在合適的時機(jī),pull method up(復(fù)用)或者 pull method down(特殊邏輯)。

***,把不屬于流程的、但可復(fù)用的方法,判斷是不是屬于基類的領(lǐng)域職責(zé),再使用繼承或者組合的方法,為這些方法找到合適的安家之處。

extract method

很多復(fù)用的級別沒有這么大,也許只是幾行相同的邏輯被copy了好幾次,何不嘗試提取方法(private)。又能明確方法行為,又能做到代碼復(fù)用,何樂不為?

責(zé)任鏈

經(jīng)常看到這樣的代碼,一連串類似的行為,只是數(shù)據(jù)或者行為不一樣。如一堆校驗器,如果成功怎么樣、失敗怎么樣;或者一堆對象構(gòu)建器,各去構(gòu)造一部分?jǐn)?shù)據(jù)。碰到這種場景,我總是喜歡定義一個通用接口,入?yún)⑹峭暾囊r灒瘶?gòu)造的參數(shù),
出參是成功/失敗的標(biāo)示或者是void。然后有很多實現(xiàn)器分別實現(xiàn)這個接口,再用一個集合把這堆行為串起來。***,遍歷這個集合,串行或者并行的執(zhí)行每一部分的邏輯。

這樣做的好處是:
① 很多通用的代碼可以在責(zé)任鏈原子對象的基類里實現(xiàn);
② 代碼清晰,開閉原則,每當(dāng)有新的行為產(chǎn)生的時候,只需要定義行的實現(xiàn)類并添加到集合里即可;
③ 為并行提供了基礎(chǔ)。

為集合顯式定義它的行為

集合是個有意思的東西,本質(zhì)上它是個容器,但由于泛型的存在,它變成了可以承載所有對象的容器。很多非集合的類,我們可以定義清楚他們的邊界和行為劃分,但是裝進(jìn)集合里,它們卻都變成了一個樣子。不停地有代碼,各種循環(huán)集合,做一些相似的操作。

其實很多時候,可以把對集合的操作顯示地封裝起來,讓它變得更有血有肉。

例如一個Map,它可能表示一個配制、一個緩存等等。如果所有的操作都是直接操作Map,那么它的行為就沒有任何語義。***,讀起來就必須要深入細(xì)節(jié);第二,如果想從獲取配置讀取緩存的地方加個通用的邏輯,例如打個log什么的,你可以想象是多么的崩潰。

個人提倡的做法是,對于有明確語義的集合的一些操作,尤其是全局的集合或者被經(jīng)常使用的集合,做一些封裝和抽象,如把Map封裝成一個Cache類或者一個config類,再提供GetFromCache這樣的方法。

總結(jié)

本文從clean code的幾個大前提出發(fā),然后提出了實踐clean code的一些手段,重點放在促成clean code的一些常用編碼和重構(gòu)技巧。
當(dāng)然,這些只代表筆者本人的一點點感悟。好的代碼,最最需要的,還是大家不斷追求卓越的精神。歡迎大家一起探索交流這個領(lǐng)域,為clean code提供更多好的思路與方法。

作者簡介

王燁,現(xiàn)在是美團(tuán)點評旅游后臺研發(fā)組的工程師,之前曾經(jīng)在百度、去哪兒和優(yōu)酷工作過,專注Java后臺開發(fā)。對于網(wǎng)絡(luò)編程和并發(fā)編程具有濃厚的興趣,曾經(jīng)做過一些基礎(chǔ)組件,也翻過一些源碼,屬于比較典型的宅男技術(shù)控。期待能夠與更多知己,在coding的路上并肩前行~
聯(lián)系郵箱:wangye03@meituan.com

責(zé)任編輯:張燕妮 來源: 美團(tuán)點評技術(shù)博客
相關(guān)推薦

2017-11-16 15:18:42

Clean Code技巧代碼

2020-06-10 08:37:21

JavaScript重構(gòu)技巧

2021-09-03 23:01:58

CSS 技巧代碼重構(gòu)

2024-10-14 08:09:08

2022-03-03 20:57:53

代碼編輯器VS code

2020-09-23 10:09:43

Dockerfile

2020-06-09 09:13:12

JavaScript重構(gòu)對象

2020-11-25 07:43:07

Java

2022-06-07 23:28:05

線程安全后端

2021-10-12 23:10:58

UnsafeJavaJDK

2021-11-10 18:52:42

SQL技巧優(yōu)化

2022-04-07 12:13:22

技巧高可用單機(jī)版

2022-03-11 19:54:07

Svelte應(yīng)用程序JavaScript

2021-10-11 09:41:20

React位運算技巧前端

2022-03-29 07:52:21

運用技巧二分查找

2024-02-26 00:00:00

架構(gòu)老化重構(gòu)

2013-09-08 22:12:02

EF Code Fir數(shù)據(jù)遷移MVC架構(gòu)設(shè)計

2010-09-06 09:06:22

CSS

2021-11-18 08:20:22

接口索引SQL

2020-05-27 11:25:48

開發(fā)技能代碼
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国产高清免费视频 | 欧洲精品视频一区 | a毛片视频网站 | 99re视频在线 | 久久久激情视频 | 亚洲精品一区二区三区在线 | av日韩精品 | 99久久久久 | 亚洲国产成人在线视频 | 婷婷色国产偷v国产偷v小说 | 免费av直接看 | 亚洲欧美精品在线 | 欧美日韩高清 | 中文字幕一区二区三区乱码图片 | 国产精品免费在线 | 日本超碰| 亚洲国产精品一区在线观看 | 亚洲逼院| 亚洲精品九九 | 成人片网址 | 中文字幕99 | 91久久精品一区二区二区 | 亚洲一二三区精品 | 久在线精品视频 | 国产成人精品一区二区 | 国产精品无码久久久久 | 欧美99| 欧美一区二区三区四区视频 | 性一交一乱一伦视频免费观看 | 国产97视频在线观看 | 国产a区| 在线免费观看黄网 | 免费视频一区 | 久久另类视频 | 成人av在线播放 | 日本免费一区二区三区四区 | 国产精品99久久久久久宅男 | 午夜影视| 我要看免费一级毛片 | 亚洲午夜电影 | 99精品网站 |