• <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>
            隨筆-60  評(píng)論-98  文章-0  trackbacks-0

            我的手邊文章,經(jīng)常隨手拿起一讀,一讀必有收獲。QT凝結(jié)了眾多軟件大家的良思妙想,關(guān)于API的設(shè)計(jì)上也有自己獨(dú)到的見(jiàn)解。閑言少敘,文章開(kāi)始... 

             

            設(shè)計(jì)Qt風(fēng)格的C++API

            作者M(jìn)atthias Ettrich,譯者Googol Lee,原文地址在這里。

            在奇趣(Trolltech),為了改進(jìn)Qt的開(kāi)發(fā)體驗(yàn),我們做了大量的研究。這篇文章里,我打算分享一些我們的發(fā)現(xiàn),以及一些我們?cè)谠O(shè)計(jì)Qt4時(shí)用到的原則,并且展示如何把這些原則應(yīng)用到你的代碼里。

            設(shè)計(jì)應(yīng)用程序接口,API,是很難的。這是一門(mén)和設(shè)計(jì)語(yǔ)言同樣難的藝術(shù)。這里可以選擇太多的原則,甚至有很多原則和其他原則有矛盾。

            現(xiàn)在,計(jì)算機(jī)科學(xué)教育把很大的力氣放在算法和數(shù)據(jù)結(jié)構(gòu)上,而很少關(guān)注設(shè)計(jì)語(yǔ)言和框架背后的原則。這讓?xiě)?yīng)用程序員完全沒(méi)有準(zhǔn)備去面對(duì)越來(lái)越重要的任務(wù):創(chuàng)造可重用的組件。

            在面向?qū)ο笳Z(yǔ)言普及之前,可重用的通用代碼大部分是由庫(kù)提供者寫(xiě)的,而不是應(yīng)用程序員。在Qt的世界里,這種狀況有了明顯的改善。在任何時(shí)候,用Qt編程就是寫(xiě)新的組件。一個(gè)典型的Qt應(yīng)用程序至少都會(huì)有幾個(gè)在程序中反復(fù)使用的自定義組件。一般來(lái)說(shuō),同樣的組件會(huì)成為其他應(yīng)用程序的一部分。KDE,K桌面環(huán)境,走得更遠(yuǎn),用許多追加的庫(kù)來(lái)擴(kuò)展Qt,實(shí)現(xiàn)了數(shù)百個(gè)附加類(lèi)。(一般來(lái)說(shuō),一個(gè)類(lèi)就是一個(gè)可重用組件,原文這里沒(méi)有寫(xiě)清楚。)

            但是,一個(gè)好的,高效的C++ API是由什么組成的呢?是好還是壞,取決于很多因素——比如,手頭的工作和特定的目標(biāo)群體。好的API有很多特性,一些特性是大家都想要的,而另一些則是針對(duì)特定問(wèn)題域的。

            好的API的六個(gè)特性

            API是面向程序員的,用來(lái)描述提供給最終用戶的GUI是什么樣子。API中的P帶表程序員(Programmer),而不是程序(Program),用來(lái)強(qiáng)調(diào)API是給程序員用的,給人類(lèi)的程序員用的。

            我們堅(jiān)信API應(yīng)該是最小化且完整的,擁有清晰且簡(jiǎn)單的語(yǔ)義,直覺(jué)化,容易記憶,并且引導(dǎo)人寫(xiě)出易讀的代碼。

            • 最小化:最小化的API是指一個(gè)類(lèi)盡可能只擁有最少的公開(kāi)成員且盡可能只擁有最少的類(lèi)。這個(gè)原則可以讓API更簡(jiǎn)單易懂,更好記,更容易除錯(cuò),且更容易改變。
            • 完整的:完整的API是指要提供所有期望的功能。這個(gè)可能與最小化原則相沖突。另外,如果一個(gè)成員函數(shù)屬于一個(gè)不應(yīng)該屬于的類(lèi),很多潛在的使用者都會(huì)找不到這個(gè)函數(shù)。
            • 擁有清晰且簡(jiǎn)單的語(yǔ)義:就像其他設(shè)計(jì)工作一樣,你必須遵守最小驚奇原則(the principle of least surprise)。讓常見(jiàn)的任務(wù)簡(jiǎn)單易行。不常見(jiàn)的工作可行,但不會(huì)讓用戶過(guò)分關(guān)注。解決特殊問(wèn)題時(shí),不要讓解決方案沒(méi)有必要的過(guò)度通用。(比如,Qt3中的QMimeSourceFactory可以通過(guò)調(diào)用QImageLoader來(lái)實(shí)現(xiàn)不同的API。)
            • 直覺(jué)化:就像電腦上的其他東西一樣,API必須是直覺(jué)化的。不同的經(jīng)驗(yàn)和背景會(huì)導(dǎo)致在判斷什么是直覺(jué)而什么不是時(shí)不同的感覺(jué)。如果一個(gè)中級(jí)用戶不讀文檔就可以使用(a semi-experienced user gets away without reading the documentation,沒(méi)懂這里的get away該怎么翻譯),并且一個(gè)程序員不懂API就可以理解縮寫(xiě)的代碼,這種API就是直覺(jué)化的。
            • 易于記憶:讓API易于記憶,使用統(tǒng)一且精確的命名方法。使用可識(shí)別的模式和概念,并且避免縮寫(xiě)。
            • 引導(dǎo)易讀的代碼(Lead to readable code):代碼一經(jīng)寫(xiě)就,會(huì)讀(并且除錯(cuò)和修改)多次。易讀的代碼可能會(huì)花點(diǎn)時(shí)間來(lái)寫(xiě),但是可以節(jié)省產(chǎn)品周期中的其他時(shí)間。

            最后,記住,不同類(lèi)型的用戶會(huì)用到API的不同部分。雖然簡(jiǎn)單的實(shí)例化一個(gè)Qt類(lèi)是非常直覺(jué)化的,讓資深專(zhuān)家在試圖子類(lèi)化之前讀一遍文檔,是很合理的。

            便利陷阱

            這是個(gè)常見(jiàn)的誤解:更好的API,用更少的代碼完成一件事。永遠(yuǎn)記住代碼一次寫(xiě)就,之后需要不斷的閱讀并理解。比如:

                QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
            0, "volume");
            

            遠(yuǎn)比下面那樣難讀(甚至難寫(xiě)):

                QSlider *slider = new QSlider(Qt::Vertical);
            slider->setRange(12, 18);
            slider->setPageStep(3);
            slider->setValue(13);
            slider->setObjectName("volume");
            

            布爾參數(shù)陷阱

            布爾參數(shù)通常會(huì)導(dǎo)致不易讀的代碼。更進(jìn)一步,給一個(gè)已經(jīng)存在的函數(shù)加入一個(gè)布爾參數(shù),這常常是個(gè)錯(cuò)誤。在Qt里,一個(gè)傳統(tǒng)的例子是repaint(),這個(gè)函數(shù)帶有一個(gè)布爾參數(shù),來(lái)標(biāo)識(shí)是否擦除背景(默認(rèn)擦除)。這讓代碼通常寫(xiě)成:

                widget->repaint(false);
            

            初學(xué)者很容易把這句話理解成“別重畫(huà)”!

            這樣做是考慮到布爾參數(shù)可以減少一個(gè)函數(shù),避免代碼膨脹。事實(shí)上,這反而增加了代碼量。有多少Q(mào)t用戶真的記住了下面三行程序都是做什么的?

                widget->repaint();
            widget->repaint(true);
            widget->repaint(false);
            

            一個(gè)好一些的API可能看起來(lái)是這樣:

                widget->repaint();
            widget->repaintWithoutErasing();
            

            在Qt4里,我們重新設(shè)計(jì)了widget,使得用戶不再需要不重畫(huà)背景的重畫(huà)widget,來(lái)解決這個(gè)問(wèn)題。Qt4原生支持雙緩存,廢掉了這個(gè)特性。

            這里還有一些例子:

                widget->setSizePolicy(QSizePolicy::Fixed,
            QSizePolicy::Expanding, true);
            textEdit->insert("Where's Waldo?", true, true, false);
            QRegExp rx("moc_*.c??", false, true);
            

            一個(gè)顯而易見(jiàn)的解決方法是,使用枚舉類(lèi)型代替布爾參數(shù)。這正是我們?cè)赒t4中QString大小寫(xiě)敏感時(shí)的處理方法。比較:

                str.replace("%USER%", user, false);               // Qt 3
            str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4
            

            靜態(tài)多態(tài)

            相似的類(lèi)應(yīng)該含有相似的API。在必要的時(shí)候——就是說(shuō),需要使用運(yùn)行時(shí)多態(tài)的時(shí)候——這可以通過(guò)繼承實(shí)現(xiàn)。但是多態(tài)依舊會(huì)發(fā)生在設(shè)計(jì)時(shí)期。比如,如果你用QListBox代替QComboBox,或者用QSlider代替QSpinBox,你會(huì)發(fā)現(xiàn)相似的API使這種替換非常容易。這就是我們所說(shuō)的“靜態(tài)多態(tài)”。

            靜態(tài)多態(tài)也使API和程序模式更容易記憶。作為結(jié)論,一組相關(guān)類(lèi)使用相似的API,有時(shí)要比給每個(gè)類(lèi)提供完美的單獨(dú)API,要好。

            (譯注:C++ 0x將要引入的concept,就是靜態(tài)多態(tài)的語(yǔ)法層實(shí)現(xiàn)。這個(gè)要比單獨(dú)的函數(shù)名相似更強(qiáng)大且易用。)

            命名的藝術(shù)

            命名,大概是設(shè)計(jì)API時(shí)唯一最重要的問(wèn)題了。該怎么稱(chēng)呼這個(gè)類(lèi)?成員函數(shù)該叫什么?

            通用的命名規(guī)則

            一些規(guī)則通常對(duì)所有名字都是有用的。首先,就像我之前提到的,別用縮寫(xiě)。甚至很明顯的縮寫(xiě),比如“prev”表示“previous”從長(zhǎng)遠(yuǎn)看也是不劃算的,因?yàn)橛脩舯仨氂涀∧男┰~是縮寫(xiě)。

            如果API本身不一致,事情自然會(huì)變得很糟糕,比如, Qt3有activatePreviousWindow()和fetchPrev()。堅(jiān)持“沒(méi)有縮寫(xiě)”的規(guī)則更容易創(chuàng)建一致的API。

            另一個(gè)重要但更加微妙的規(guī)則是,在設(shè)計(jì)類(lèi)的時(shí)候,必須盡力保證子類(lèi)命名空間的干凈。在Qt3里,沒(méi)有很好的遵守這個(gè)規(guī)則。比如,拿QToolButton來(lái)舉例。如果你在Qt3里,對(duì)一個(gè)QToolButton調(diào)用name()、caption()、text()或者textLabel(),你希望做什么呢?你可以在Qt Designer里拿QToolButton試試:

            • name屬性繼承自QObject,表示一個(gè)對(duì)象用于除錯(cuò)和測(cè)試的內(nèi)部名字。
            • caption屬性繼承自QWidget,表示窗口的標(biāo)題,這個(gè)標(biāo)題在視覺(jué)上對(duì)QToolButton沒(méi)有任何意義,因?yàn)樗麄兛偸歉S父窗口而創(chuàng)建。
            • text屬性繼承自QButton,一般情況下是按鈕上現(xiàn)實(shí)的文字,除非useTextLabel為真。
            • textLabel在QToolButton里聲明,并且在useTextLabel為真時(shí)顯示在按鈕上。

            由于對(duì)可讀性的關(guān)注,name在Qt4里被稱(chēng)作objectName,caption變成了windowsTitle,而在QToolButton里不再有單獨(dú)的textLabel屬性。

            給類(lèi)命名

            標(biāo)識(shí)一組類(lèi)而不是單獨(dú)給每個(gè)類(lèi)找個(gè)恰當(dāng)?shù)拿?。比如,Qt4里所有模式感知項(xiàng)目的視圖類(lèi)(model-aware item view classes)都擁有-View的后綴(QListView、QTableViewQTreeView),并且對(duì)應(yīng)基于項(xiàng)目的類(lèi)都用后綴-Widget代替(QListWidget、QTableWidgetQTreeWidget)。

            給枚舉類(lèi)型及其值命名

            當(dāng)聲明枚舉時(shí),時(shí)刻記住,在C++(不像Java和C#)中,使用枚舉值不需要類(lèi)型信息。下面的例子演示了給枚舉值起個(gè)太過(guò)常用的名字所引起的危害:

                namespace Qt
            {
            enum Corner { TopLeft, BottomRight, ... };
            enum CaseSensitivity { Insensitive, Sensitive };
            ...
            };
            tabWidget->setCornerWidget(widget, Qt::TopLeft);
            str.indexOf("$(QTDIR)", Qt::Insensitive);
            

            在最后一行,Insensitive是什么意思?一個(gè)用于命名枚舉值的指導(dǎo)思想是,在每個(gè)枚舉值里,至少重復(fù)一個(gè)枚舉類(lèi)型名中的元素:

                namespace Qt
            {
            enum Corner { TopLeftCorner, BottomRightCorner, ... };
            enum CaseSensitivity { CaseInsensitive,
            CaseSensitive };
            ...
            };
            tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
            str.indexOf("$(QTDIR)", Qt::CaseInsensitive);
            

            當(dāng)枚舉值可以用“或”連接起來(lái)當(dāng)作一個(gè)標(biāo)志時(shí),傳統(tǒng)的做法是將“或”的結(jié)果作為一個(gè)int保存,這不是類(lèi)型安全的。Qt4提供了一個(gè)模板類(lèi) QFlags<T>來(lái)實(shí)現(xiàn)類(lèi)型安全,其中T是個(gè)枚舉類(lèi)型。為了方便使用,Qt為很多標(biāo)志類(lèi)名提供了typedef,所以你可以使用類(lèi)型 Qt::Alignment代替QFlags<Qt::AlignmentFlag>。

            為了方便,我們給枚舉類(lèi)型單數(shù)的名字(這樣表示枚舉值一次只能有一個(gè)標(biāo)志),而“標(biāo)志”則使用復(fù)數(shù)名字。比如:

                enum RectangleEdge { LeftEdge, RightEdge, ... };
            typedef QFlags<RectangleEdge> RectangleEdges;
            

            有些情況下,“標(biāo)志“類(lèi)使用了單數(shù)的名字。這時(shí),枚舉類(lèi)使用-Flag做后綴:

                enum AlignmentFlag { AlignLeft, AlignTop, ... };
            typedef QFlags<AlignmentFlag> Alignment;
            

            (這里為啥不是把”標(biāo)志“類(lèi)用-Flag做后綴,而是把枚舉值做后綴呢?感覺(jué)有點(diǎn)混淆……)

            給函數(shù)和參數(shù)命名

            給函數(shù)命名的一個(gè)規(guī)則是,名字要明確體現(xiàn)出這個(gè)函數(shù)是否有副作用。在Qt3,常數(shù)函數(shù)QString::simplifyWhiteSpace()違反了這個(gè)原則,因?yàn)樗祷仡?lèi)一個(gè)QString實(shí)例,而不是像名字所提示的那樣,更改了調(diào)用這個(gè)函數(shù)的實(shí)例本身。在Qt4,這個(gè)函數(shù)被重命名為QString::simplified()。

            參數(shù)名是程序員的重要信息來(lái)源,雖然在使用API時(shí),并不直接展示在代碼里。由于現(xiàn)代IDE在程序員寫(xiě)代碼時(shí)可以自動(dòng)顯示參數(shù)名(就是自動(dòng)感知或者自動(dòng)補(bǔ)全之類(lèi)的功能),值得花時(shí)間給頭文件里聲明的參數(shù)一個(gè)合適的名字,并且在文檔中也使用相同的名字。

            給布爾值設(shè)置函數(shù)(Setter)、提取函數(shù)(Getter)和屬性命名

            給布爾屬性的設(shè)置函數(shù)和提取函數(shù)一個(gè)合適的名字,總是非常痛苦的。提取函數(shù)應(yīng)該叫做checked()還是isChecked()?scrollBarsEnabled()還是areScrollBarEnabled()?

            在Qt4里,我們使用下列規(guī)則命名提取函數(shù):

            • 形容類(lèi)的屬性使用is-前綴。比如:
              • isChecked()
              • isDown()
              • isEmpty()
              • isMovingEnable()
              另外,應(yīng)用到復(fù)數(shù)名詞的形容類(lèi)屬性沒(méi)有前綴:
              • scrollBarsEnabled(),而不是areScrollBarsEnabled()
            • 動(dòng)詞類(lèi)的屬性不使用前綴,且不使用第三人稱(chēng)(-s):
              • acceptDrops(),而不是acceptsDrops()
              • allColumnsShowFocus()
            • 名詞類(lèi)的屬性,通常沒(méi)有前綴:
              • autoCompletion(),而不是isAutoCompletion()
              • boundaryChecking()
              有時(shí),沒(méi)有前綴就會(huì)引起誤解,這種情況使用前綴is-:
              • isOpenGLAvailable(),而不是openGL()
              • isDialog(),而不是dialog()
              (通過(guò)調(diào)用dialogue()方法,正常情況下會(huì)期望返回一個(gè)QDialog*的實(shí)例。)

            設(shè)置函數(shù)名字繼承自提取函數(shù)名,只是移掉了所有前綴,并使用set-做前綴,比如:setDown()還有setScrollBarsEnabled()。屬性的名字與提取函數(shù)相同,只是去掉了前綴。

            指針還是引用?

            傳出參數(shù)的最佳選擇是什么,指針還是引用?

                void getHsv(int *h, int *s, int *v) const
            void getHsv(int &h, int &s, int &v) const
            

            大部分C++書(shū)推薦在能用引用的地方就用引用,這是因?yàn)橐话阏J(rèn)為引用比指針更“安全且好用”。然而,在奇趣(Trolltech),我們傾向使用指針,因?yàn)檫@讓代碼更易讀。比較:

                color.getHsv(&h, &s, &v);
            color.getHsv(h, s, v);
            

            只有第一行能清楚的說(shuō)明,在函數(shù)調(diào)用后,h、s和v將有很大幾率被改動(dòng)。

            例子:QProgressBar

            為了展示如何實(shí)際應(yīng)用這些概念,我們將學(xué)習(xí)Qt3中的API QProgressBar并和Qt4里相通的API做比較。在Qt3里:

                class QProgressBar : public QWidget
            {
            ...
            public:
            int totalSteps() const;
            int progress() const;
            const QString &progressString() const;
            bool percentageVisible() const;
            void setPercentageVisible(bool);
            void setCenterIndicator(bool on);
            bool centerIndicator() const;
            void setIndicatorFollowsStyle(bool);
            bool indicatorFollowsStyle() const;
            public slots:
            void reset();
            virtual void setTotalSteps(int totalSteps);
            virtual void setProgress(int progress);
            void setProgress(int progress, int totalSteps);
            protected:
            virtual bool setIndicator(QString &progressStr,
            int progress,
            int totalSteps);
            ...
            };
            

            API相當(dāng)復(fù)雜,且不統(tǒng)一。比如,僅從名字reset()并不能理解其作用,setTotalSteps()和setProgress()是緊耦合的。

            改進(jìn)API的關(guān)鍵,是注意到QProgressBar和Qt4的QAbstractSpinBox類(lèi)及其子類(lèi)QSpinBox,QSliderQDial很相似。解決方法?用minimum、maximum和value代替progress和totalSteps。加入alueChanged()信號(hào)。加入setRange()函數(shù)。

            之后觀察progressString、percentage和indicator實(shí)際都指一個(gè)東西:在進(jìn)度條上顯示的文字。一般來(lái)說(shuō)文字是百分比信息,但是也可以使用setIndicator()設(shè)為任意字符。下面是新的API:

                virtual QString text() const;
            void setTextVisible(bool visible);
            bool isTextVisible() const;
            

            默認(rèn)的文字信息是百分比信息。文字信息可以藉由重新實(shí)現(xiàn)text()而改變。

            在Qt3 API中,setCenterIndicator()和setIndicatorFollowStyle()是兩個(gè)影響對(duì)齊的函數(shù)。他們可以方便的由一個(gè)函數(shù)實(shí)現(xiàn),setAlignment():

                void setAlignment(Qt::Alignment alignment);
            

            如果程序員不調(diào)用setAlignment(),對(duì)齊方式基于當(dāng)前的風(fēng)格。對(duì)于基于Motif的風(fēng)格,文字將居中顯示;對(duì)其他風(fēng)格,文字將靠在右邊。

            這是改進(jìn)后的QProgressBar API:

                class QProgressBar : public QWidget
            {
            ...
            public:
            void setMinimum(int minimum);
            int minimum() const;
            void setMaximum(int maximum);
            int maximum() const;
            void setRange(int minimum, int maximum);
            int value() const;
            virtual QString text() const;
            void setTextVisible(bool visible);
            bool isTextVisible() const;
            Qt::Alignment alignment() const;
            void setAlignment(Qt::Alignment alignment);
            public slots:
            void reset();
            void setValue(int value);
            signals:
            void valueChanged(int value);
            ...
            };
            

            如何把API設(shè)計(jì)好(原文是How to Get APIs Right,我總想成We do APIs right……)

            API需要質(zhì)量保證。第一個(gè)修訂版不可能是正確的;你必須做測(cè)試。寫(xiě)些用例:看看那些使用了這些API的代碼,并驗(yàn)證代碼是否易讀。

            其他的技巧包括讓別的人分別在有文檔和沒(méi)有文檔的情況下,使用這些API;或者為API類(lèi)寫(xiě)文檔(包括類(lèi)的概述和獨(dú)立的函數(shù))。

            當(dāng)你卡住時(shí),寫(xiě)文檔也是一種獲得好名字的方法:僅僅是嘗試把條目(類(lèi),函數(shù),枚舉值,等等呢個(gè))寫(xiě)下來(lái)并且使用你寫(xiě)的第一句話作為靈感。如果你不能找到一個(gè)精確的名字,這常常說(shuō)明這個(gè)條目不應(yīng)該存在。如果所有前面的事情都失敗了并且你確認(rèn)這個(gè)概念的存在,發(fā)明一個(gè)新名字。畢竟,“widget”、 “event”、“focus”和“buddy”這些名字就是這么來(lái)的。

            posted on 2008-10-20 11:32 創(chuàng)建更好的解決方案 閱讀(775) 評(píng)論(1)  編輯 收藏 引用

            評(píng)論:
            # re: 【zz】設(shè)計(jì)Qt風(fēng)格的C++API 2010-06-30 14:47 | 項(xiàng)冠南
            我用過(guò)Qt 4,感覺(jué)非常好,使用方便,擴(kuò)展性強(qiáng)。

            奇趣工程師的態(tài)度,讓人很欽佩,不是一味的按書(shū)本上說(shuō)的去做,每個(gè)地方都仔細(xì)斟酌。。。

            Qt 4是我見(jiàn)過(guò)設(shè)計(jì)最好的GUI API了。。。。。  回復(fù)  更多評(píng)論
              
            91超碰碰碰碰久久久久久综合| A级毛片无码久久精品免费| 热久久国产欧美一区二区精品| 国内精品久久久久久久涩爱| 亚洲v国产v天堂a无码久久| 久久亚洲AV成人出白浆无码国产 | 久久夜色撩人精品国产| 中文国产成人精品久久亚洲精品AⅤ无码精品| 精品久久久久久无码人妻热| 一本一本久久A久久综合精品| 99久久精品免费看国产| 久久人妻无码中文字幕| 国产成人精品久久亚洲高清不卡 | 亚洲国产二区三区久久| 国产精品久久新婚兰兰| 欧美777精品久久久久网| 国产aⅴ激情无码久久| 久久久久亚洲AV无码专区网站| av午夜福利一片免费看久久| 亚洲一区精品伊人久久伊人| 99久久免费国产精品| 日产精品久久久久久久性色| 久久精品综合网| 日韩久久久久中文字幕人妻 | 久久精品国产99久久久古代| 国产精品成人久久久久三级午夜电影| 久久综合狠狠综合久久综合88| 思思久久好好热精品国产| 久久综合成人网| 久久精品国产亚洲一区二区三区| 国产精品久久久久jk制服| 伊人伊成久久人综合网777| 久久毛片免费看一区二区三区| 国产成人精品久久亚洲高清不卡| 久久精品嫩草影院| 久久精品视频网| 四虎国产永久免费久久| 国产成人精品久久亚洲| 久久www免费人成看国产片| 久久综合给合综合久久| 久久天天躁狠狠躁夜夜2020一|