• <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>
            posts - 24,  comments - 62,  trackbacks - 0

            "Designing Qt-Style C++ APIs" by Matthias Ettrich

            http://doc.trolltech.com/qq/qq13-apis.html

            翻譯這篇文章的目的不是讓人了解Qt,而是讓人試著學(xué)習(xí)點C++編程的軟技能。我從原文中得到的一些風(fēng)格上的體會,也希望你能從中有所收獲.(譯者注)
            我們在Trolltech做了大量研究來改進(jìn)Qt開發(fā)體驗.在這篇文章中,我將分享我們的一些成果,呈現(xiàn)我們在進(jìn)行Qt 4設(shè)計時所使遵循的原現(xiàn),并向你展示如何將它們應(yīng)用到你的代碼中.

            設(shè)計應(yīng)用程序接口(APIs)是有難度的.它是像跟設(shè)計編程語言一樣困難的藝術(shù).要遵循許多不同的的原則,這些原則中的許多還彼此沖突.

            現(xiàn)今的計算機教育過多關(guān)注于算法和數(shù)據(jù)結(jié)構(gòu),很少去關(guān)注隱藏在程序設(shè)計語言和程序框架后面的那些設(shè)計原則.這使得程序員們面對日益重要的任務(wù),創(chuàng)建可復(fù)用的組件,毫無準(zhǔn)備.

            在面向?qū)ο笳Z言出現(xiàn)前,通用的可復(fù)用的代碼大都由庫提供者而不是應(yīng)用程序開發(fā)者來編寫.在Qt世界中,這種情況已發(fā)生了很大的變化.在用Qt編程其實就是在寫新的組件.典型的Qt應(yīng)用程序都存在某些自定義的組件,在整個應(yīng)用程序中被復(fù)用.相同的組件常常作為其他程序的一部分被開發(fā)出來.KDE,K桌面環(huán)境,甚至使用許多附加庫,來進(jìn)一步擴(kuò)展Qt,實現(xiàn)許多額外的類.

            但是一個優(yōu)秀,高效的C++ API究竟是怎樣子呢?它的好壞取決于許多因素,比如說,手頭上的任務(wù)和特定目標(biāo)群體.優(yōu)秀的API具有很多特性,它們的一些是普遍所要期望的,另一些是針對特定問題域的.

            優(yōu)秀API的六個特性

            API對于程序員就相當(dāng)于GUI對于最終用戶.API中'P'代表程序員(Programmer),而不是程序(Program),強調(diào)這一點是為了說明API是讓程序員使用的,程序員是人而不機器.

            我們認(rèn)為APIs應(yīng)當(dāng)精簡而完備,具有清晰簡單的語義,直觀,易記且應(yīng)使代碼具有可讀性.

            • 精簡性:精簡的API具有盡可能少的類和公共成員.這使得理解,記憶,調(diào)試,更改API更加容易.
            • 完備性:完備的API意味著擁有應(yīng)具有的期望功能.這可能使與API保持精簡性相沖突.還有,如果成員函數(shù)放在不相匹配的類中,那么許多使用這個功能函數(shù)的潛在用戶會找不到它.
            • 清晰簡單的語義:正如與其他設(shè)計工作一樣,你應(yīng)該準(zhǔn)守最小驚議原則.讓通常的任務(wù)簡單,罕見的任務(wù)應(yīng)盡可能簡單,但它不應(yīng)成為重點.解決特定的問題.不要使解決方法具有普適作用,當(dāng)它們不需要的時候.
            • 直觀性:與計算機有關(guān)的其他事情一樣,API應(yīng)具有直觀性.不同經(jīng)歷和背景會導(dǎo)致對哪些是直觀,哪些不是直觀的不同看法.如果對非專業(yè)的用戶在不需要閱讀文檔下能立即使用API,或?qū)@個API不了解的程序員能理解使用了API的代碼,那么這API就是具有直觀性.
            • 易記:為了使API容易記憶,使用一致且精準(zhǔn)的命名規(guī)范.使用容易識別的模式和概念,避免使用縮寫.
            • 能生成可讀生代碼:代碼只寫一遍,卻要閱讀許多遍(調(diào)試或更改).可讀性的代碼有時候可能需要多敲些字,但是從產(chǎn)品生命周期中可節(jié)省很多時間.

            最后,請記住:不同的用戶使用API的不同部分.當(dāng)簡單地使用Qt類的實例可能有直觀性,但這有可能使用戶在閱讀完有關(guān)文檔后,才能嘗試使用其中部分功能.

            方便性陷阱

            通常的誤讀是越少的代碼越能使你達(dá)到編寫更好的API這一目的.請記住,代碼只寫一遍,卻要一遍又一遍地去理解閱讀它.比如:

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

            可以會比下面的代碼更難閱讀(甚至于編寫)

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

            布爾參數(shù)陷阱

            布爾參數(shù)常常導(dǎo)致難以閱讀的代碼.特別地,增加某個bool參數(shù)到現(xiàn)存的函數(shù)一般都會是個錯誤的決定.在Qt中,傳統(tǒng)的例子是repaint(),它帶有一個可選的布爾參數(shù),來指定背景是否刪除(默認(rèn)是刪除).這就導(dǎo)致了代碼會像這樣子:

            widget->repaint(false);

            初學(xué)者可能會按字面義理解為,"不要重繪!"

            自然的想法是bool參數(shù)節(jié)省了一個函數(shù),因此減少了代碼的臃腫.事實上,這增加了代碼的臃腫,有多少Q(mào)t用戶真正知道下面這三行代碼在做什么呢?

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

            好一點的API代碼可能看起來像這樣:

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

            在Qt 4中,我們解決這個問題的辦法是,簡單地去除掉不刪除widget而進(jìn)行重繪的可能性.Qt 4對雙重緩沖的原生支持,會使這功能被廢棄掉.

            這里有些例子:

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

            顯然的解決辦法就是將bool 參數(shù)用枚舉類型來替換.這就是我們在Qt 4中Qstring中的大小寫敏感所做的,比較下面兩個例子:

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

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

            相似的類應(yīng)該有相似的API.在某種程度上,這能用繼承來實現(xiàn),也就是運用運行時多態(tài)機制.但是多態(tài)也能發(fā)生在設(shè)計時.比如,你將QListBox與QComboBox交換,QSlider與QSpinBox交換,你會發(fā)現(xiàn)API的相似性會使這種替換變得比較容易.這就是我們所謂的"靜態(tài)多態(tài)".

            靜態(tài)多態(tài)也能使記憶API和編程模式更加容易.因而,對一組相關(guān)類的相似API有時候比為每個類設(shè)計獨特完美的API會更好.

            命名藝術(shù)

            命名有時候是設(shè)計API中最重要的事情了.某個類應(yīng)叫什么名字,某個成員函數(shù)又應(yīng)叫什么名字,都需要好好思考.?

            通常的命名規(guī)則

            有少許規(guī)則對所有類型的命名都適應(yīng).首先,正如我早先所提到的,不要用縮寫.甚至對用"prev"代表"previous"這樣明顯的縮寫也不會在長期中受益,因為用戶必須記住哪些名字是縮寫.

            如果連API自身都不能保持統(tǒng)一,事情自然會變得更壞.比如,Qt 3中有activatePreviousWindow()函數(shù),也有fetchPrev()函數(shù).堅持"沒有縮寫"這條規(guī)則,會使創(chuàng)建一致的API更加簡單.

            在設(shè)計類中,另一重要但是不明顯的規(guī)則是盡量保持子類中名字的簡潔易懂.在Qt 3中,這個原則并不總是被遵守.為了說明這一點,我們舉下QToolButton的例子.如果你在Qt 3中對QToolButton調(diào)用call name(), caption(), text(), 或 textLabel()成員函數(shù)時,你希望會發(fā)生什么?那就在Qt設(shè)計器中試試QToolButton吧.

            • name 屬性繼承自QObject,用來在調(diào)試和測試中指代對象的內(nèi)部名稱.
            • caption 屬性繼承自QWidget,指代窗體的標(biāo)題.對于QToolButton沒有什么意思,既然它們都是由父窗體創(chuàng)建的.
            • text 屬性繼承自QButton,通常用于按鈕中,除非useTextLabel為真.
            • textLabel 屬性 在QToolButton中聲明,如果useTextLabel為真,則顯示在按鈕上.

            為了可讀性的關(guān)系,在Qt4中name 被稱為objectName ,caption被稱為windowTitle,在QToolButton中為了使text明晰,不再有textLabel屬性.

            命名類

            不應(yīng)為每個不同的類尋求完美的名字,而是將類進(jìn)行分給.比如,在Qt 4中所有跟模型有關(guān)的視類的部件都用View后綴(QlistView,QTableView,QTreeView),相應(yīng)的基于部件的類用Widget后綴代替(QListWidget,QTableWidget,QTreeWidge).

            枚舉類型和值類型命名

            當(dāng)設(shè)計枚舉時,我們應(yīng)當(dāng)記住C++中(不像Java或C#),枚舉值在使用時不帶類型名.下面的例子說明了對枚舉值取太一般化的名字的危害:

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

            在上面這行中,Insensitive這個名字什么意思呢?為枚舉類型命名具有指導(dǎo)的原則是最好在每個枚舉值中重復(fù)枚舉類型的名字.

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

            但枚舉值之間是一種"或"關(guān)系和被用作標(biāo)志位時,傳統(tǒng)的解決方法是將"或"結(jié)果存為int,這樣做是類型不安全的.Qt 4提供了一模板類QFlags<T>,其中T是枚舉類型.Qt為標(biāo)志類型名稱提供了便利,你能用Qt::Alignment 來代替QFlags<Qt::AlignmentFlag>.

            為了方便,我們給枚舉類型單數(shù)形式的名稱(只有當(dāng)只含一個標(biāo)志位時),給"flags"類型復(fù)數(shù)形式的名稱,比如:

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

            在某些情況下,"flags"類型有單數(shù)形式的名稱.在這種情況下,枚舉類型以Flag后綴標(biāo)識:

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

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

            函數(shù)命名中的一條規(guī)則就是應(yīng)能從它的名字清楚地看出函數(shù)是否著副作用.在Qt 3中,常函數(shù)QString::simplifyWhiteSpace()就違反了這規(guī)則.即然它返回QString,而不是像它的名字所表述的那樣修改字符串. 在Qt 4中,這個函數(shù)被重命名為QString::simplified().

            參數(shù)名對于程序員來說是重要的信息來源,即使它們不出現(xiàn)在調(diào)用API的代碼中.既然現(xiàn)代的IDE會在程序員編碼時顯示這些參數(shù),所以非常值得在頭文件中給這些參數(shù)取恰當(dāng)?shù)拿郑谖臋n中同樣使用相同的名字

            給布爾型的getter,setter,屬性的命名

            給布爾型的getter,setter,屬性取個恰當(dāng)?shù)拿挚偸翘貏e困難.getter應(yīng)該叫checked() 或者還是叫isChecked(),取scrollBarsEnabled()還是areScrollBarEnabled()

            在Qt 4中,我們對于getter的函數(shù)使用下面的指導(dǎo)原則

            • 形容詞就使用is-前綴.比如:
              • isChecked()
              • isDown()
              • isEmpty()
              • isMovingEnabled()
              但是形容詞應(yīng)用到復(fù)數(shù)形式的名詞沒有前綴:
              • scrollBarsEnabled(), not areScrollBarsEnabled()
            • 動詞沒有前綴,也不使用第三人稱的(-s):
              • acceptDrops(), not acceptsDrops()
              • allColumnsShowFocus()
            • 名詞性的通常沒有前綴:
              • autoCompletion(), 不用isAutoCompletion()
              • boundaryChecking()
              有時候沒有前綴會產(chǎn)生誤導(dǎo),在這種情就加上前綴is-:
              • isOpenGLAvailable(), not openGL()
              • isDialog(), not dialog()
              (如果函數(shù)叫做dialog(),我們通常會認(rèn)定它會返回QDialog*類型)

            setter的命名可以從這推知,只要去掉is前綴,在名字前面加set前綴就可以了.比如setDown()setScrollBarsEnabled().屬性的名字跟getter一樣,就是沒有is前綴

            指針或引用?

            對于向外傳參,是使用指針,還是引用更好呢?

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

            絕大多數(shù)C++書籍都推薦無論何時都盡可能使用引用,因為從大多數(shù)情況來說,引用比指針有著所謂的"安全和優(yōu)雅".相比而方,在Trolltech,我們更趨向于指針,因為它使用戶代碼更具可讀性.比較下面的代碼:

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

            只有第一行代碼能更清楚地說明h,s,v在函數(shù)被調(diào)用后,其值極有可能被修改.

            案例分析:QProgressBar

            為了在實際代碼中說明這些概念,我們以QProgressBar在Qt3和Qt4中的比較進(jìn)行研究.在Qt 3中:

            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進(jìn)行改進(jìn)的關(guān)鍵之處就是需要觀察到Qt 4中QProgressBar與QAbstractSpinBox,以及它的子類,QSpinBox,QSlider,和QDial有著相似性.解決的辦法呢?將其中的progress和totalSteps用minimun,maximum和value替換.

            增加valueChanged()的信號量.增加setRange()這一方便的函數(shù).

            接下來需要到progressString, percentageindicator實際上都指代同一東西:顯示在進(jìn)度欄上的文本.通常這一文本是一百分?jǐn)?shù),但是它能被setIndicator()設(shè)置成任何值.這里是新的API:

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

            默認(rèn),這文本是百分比指示器.這可以用重新實現(xiàn)的text()進(jìn)行改變.

            在Qt 3中,setCenterIndicator()setIndicatorFollowsStyle()是兩個影響對齊方式的函數(shù).它們現(xiàn)在都被一個高級的函數(shù)所取代,setAlignment()

            void setAlignment(Qt::Alignment alignment);
            

            如果程序員沒有調(diào)用 setAlignment(),對齊是基于的樣式?jīng)Q定的.對于Motif樣式,文本顯示在中間,而對于其他樣式,文本是右對齊的.

            這里是改進(jìn)過的QProgressBar:

            class QProgressBar : public QWidget32
            {
                    ...
                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);
                    ...
            };

            怎樣寫出正確的APIs

            APIs需要質(zhì)量保證.最早的版本一般都不是很好的,你必須測試它.通過調(diào)用這個API的代碼作為測試事例,來驗證代碼具有可讀性.

            另外的技巧包括讓人在沒有文檔和類文檔化(類的概述和函數(shù)說明)的情況下能夠使用這個API.

            當(dāng)你陷入麻煩中時,文檔化也是好的辦法找出一個合適的命名:試著為這些類,函數(shù),枚舉值標(biāo)住文檔,然后使用浮現(xiàn)在你腦中的第一個詞匯.如果你找不到精準(zhǔn)的名字去表述,那很有可能這個東西就不應(yīng)存在.如果任何辦法都失敗了,而且你確信這個概念是有用的,那就發(fā)明一個新的名字吧.最后,不管怎么說,"widget", "event", "focus", and "buddy"這些詞總會能用上一個.

            posted on 2008-05-11 20:07 len 閱讀(7801) 評論(6)  編輯 收藏 引用 所屬分類: 程序開發(fā)

            FeedBack:
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2008-05-12 09:14 | Xw.Y
            nice :-)
            相當(dāng)不錯的介紹,雖然偶不了解Qt,但是這個很大一部分是通用的一種設(shè)計方法。
            謝謝~  回復(fù)  更多評論
              
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2008-05-12 18:57 | phenix
            謝謝譯者翻譯這篇文章。

            關(guān)于介紹API設(shè)計經(jīng)驗的文章都很有價值,其他的還有Netbeans API, Eclipse API,.Net framework等。

            謝謝!  回復(fù)  更多評論
              
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2008-05-13 09:21 | 周星星
            贊一個,很精彩,雖然我沒接觸過qt  回復(fù)  更多評論
              
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2008-05-14 10:43 | 唐新發(fā)
            在理有據(jù),一直在各種風(fēng)格間搖擺的偶找到明燈了。  回復(fù)  更多評論
              
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2008-05-15 17:25 | 買書網(wǎng)
            在沒有文檔和類文檔化的情況下能夠使用這個API挺難得  回復(fù)  更多評論
              
            # re: (翻譯)設(shè)計Qt風(fēng)格的C++的應(yīng)用程序接口
            2009-09-03 09:22 | 2007000768

            <2008年3月>
            2425262728291
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            四虎亚洲国产成人久久精品| 久久久久国产精品| 人人狠狠综合久久亚洲| 天天做夜夜做久久做狠狠| 亚洲人成电影网站久久| 久久综合九色综合网站| 青青热久久综合网伊人| 久久夜色撩人精品国产| 久久天天躁狠狠躁夜夜avapp| 久久精品九九亚洲精品| 精品99久久aaa一级毛片| 久久亚洲AV无码精品色午夜麻豆| 久久久精品2019免费观看| 久久国产福利免费| 久久棈精品久久久久久噜噜| 久久黄视频| 狠狠狠色丁香婷婷综合久久五月| 午夜视频久久久久一区 | 色诱久久久久综合网ywww| 久久精品国产半推半就| 尹人香蕉久久99天天拍| 成人a毛片久久免费播放| 精品无码久久久久久尤物| 久久综合久久美利坚合众国| 国产真实乱对白精彩久久| 国产精品久久久久久一区二区三区 | 日韩精品久久久肉伦网站| 亚洲国产精品综合久久网络 | 狠狠综合久久综合中文88| 麻豆一区二区99久久久久| 亚洲精品99久久久久中文字幕| 久久精品国产秦先生| 97久久精品无码一区二区| 亚洲中文字幕久久精品无码喷水| 亚洲国产精品综合久久网络| 久久久综合香蕉尹人综合网| 久久只有这里有精品4| 久久精品国产精品亚洲精品 | 国产精品久久影院| 国内精品九九久久精品| 久久久亚洲AV波多野结衣|