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

posts - 24,  comments - 62,  trackbacks - 0

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

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

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

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

現今的計算機教育過多關注于算法和數據結構,很少去關注隱藏在程序設計語言和程序框架后面的那些設計原則.這使得程序員們面對日益重要的任務,創建可復用的組件,毫無準備.

在面向對象語言出現前,通用的可復用的代碼大都由庫提供者而不是應用程序開發者來編寫.在Qt世界中,這種情況已發生了很大的變化.在用Qt編程其實就是在寫新的組件.典型的Qt應用程序都存在某些自定義的組件,在整個應用程序中被復用.相同的組件常常作為其他程序的一部分被開發出來.KDE,K桌面環境,甚至使用許多附加庫,來進一步擴展Qt,實現許多額外的類.

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

優秀API的六個特性

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

我們認為APIs應當精簡而完備,具有清晰簡單的語義,直觀,易記且應使代碼具有可讀性.

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

最后,請記?。翰煌挠脩羰褂肁PI的不同部分.當簡單地使用Qt類的實例可能有直觀性,但這有可能使用戶在閱讀完有關文檔后,才能嘗試使用其中部分功能.

方便性陷阱

通常的誤讀是越少的代碼越能使你達到編寫更好的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");

布爾參數陷阱

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

widget->repaint(false);

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

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

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

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

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

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

這里有些例子:

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

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

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

靜態多態

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

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

命名藝術

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

通常的命名規則

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

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

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

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

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

命名類

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

枚舉類型和值類型命名

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

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

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

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

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

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

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

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

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

函數和參數的命名

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

參數名對于程序員來說是重要的信息來源,即使它們不出現在調用API的代碼中.既然現代的IDE會在程序員編碼時顯示這些參數,所以非常值得在頭文件中給這些參數取恰當的名字,在文檔中同樣使用相同的名字

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

給布爾型的getter,setter,屬性取個恰當的名字總是特別困難.getter應該叫checked() 或者還是叫isChecked(),取scrollBarsEnabled()還是areScrollBarEnabled()

在Qt 4中,我們對于getter的函數使用下面的指導原則

  • 形容詞就使用is-前綴.比如:
    • isChecked()
    • isDown()
    • isEmpty()
    • isMovingEnabled()
    但是形容詞應用到復數形式的名詞沒有前綴:
    • scrollBarsEnabled(), not areScrollBarsEnabled()
  • 動詞沒有前綴,也不使用第三人稱的(-s):
    • acceptDrops(), not acceptsDrops()
    • allColumnsShowFocus()
  • 名詞性的通常沒有前綴:
    • autoCompletion(), 不用isAutoCompletion()
    • boundaryChecking()
    有時候沒有前綴會產生誤導,在這種情就加上前綴is-:
    • isOpenGLAvailable(), not openGL()
    • isDialog(), not dialog()
    (如果函數叫做dialog(),我們通常會認定它會返回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

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

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

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

案例分析:QProgressBar

為了在實際代碼中說明這些概念,我們以QProgressBar在Qt3和Qt4中的比較進行研究.在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進行改進的關鍵之處就是需要觀察到Qt 4中QProgressBar與QAbstractSpinBox,以及它的子類,QSpinBox,QSlider,和QDial有著相似性.解決的辦法呢?將其中的progress和totalSteps用minimun,maximum和value替換.

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

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

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

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

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

void setAlignment(Qt::Alignment alignment);

如果程序員沒有調用 setAlignment(),對齊是基于的樣式決定的.對于Motif樣式,文本顯示在中間,而對于其他樣式,文本是右對齊的.

這里是改進過的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需要質量保證.最早的版本一般都不是很好的,你必須測試它.通過調用這個API的代碼作為測試事例,來驗證代碼具有可讀性.

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

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

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

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

關于介紹API設計經驗的文章都很有價值,其他的還有Netbeans API, Eclipse API,.Net framework等。

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

<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(4)

隨筆分類

隨筆檔案

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲高清在线| 欧美视频中文一区二区三区在线观看 | 香蕉视频成人在线观看| 亚洲欧美日韩综合aⅴ视频| 亚洲精品之草原avav久久| 极品裸体白嫩激情啪啪国产精品| 欧美天天视频| 国产精品乱码妇女bbbb| 国产精品激情电影| 国产精品美女久久久| 久久久久.com| 欧美精品一区二| 欧美伦理a级免费电影| 欧美成人乱码一区二区三区| 麻豆精品国产91久久久久久| 久久综合久久综合久久综合| 亚洲淫片在线视频| 久久青草久久| 欧美激情视频在线播放| 亚洲破处大片| 亚洲欧美999| 亚洲一区二区三区成人在线视频精品| 日韩午夜中文字幕| 亚洲经典在线| 一本色道久久综合一区| 亚洲欧美在线视频观看| 午夜在线电影亚洲一区| 久久精品国产77777蜜臀| 久久精品国产亚洲aⅴ| 亚洲国产日韩欧美一区二区三区| 亚洲精选久久| 亚洲欧美制服中文字幕| 久久日韩粉嫩一区二区三区| 欧美高清视频一区二区三区在线观看| 欧美日韩成人一区二区| 欧美国产精品| 国产午夜久久| 亚洲精品在线一区二区| 亚洲欧美日韩在线不卡| 久久久久久久网站| 亚洲日本在线观看| 欧美日韩综合一区| 欧美日韩精品欧美日韩精品| 国产精品乱码久久久久久| 91久久精品国产| 久久―日本道色综合久久| 亚洲影院在线观看| 欧美精品一区在线观看| 亚洲国产美女| 欧美国产第一页| 久久久久久久一区| 一区二区视频免费在线观看| 久久激情综合网| 亚洲一区在线播放| 国产精品系列在线| 欧美一区二区精品在线| 亚洲一本视频| 国产精品一区二区久久| 亚洲欧美中日韩| 亚洲一区免费看| 国产日本欧美一区二区| 久久gogo国模裸体人体| 欧美一区二区| 激情综合中文娱乐网| 农村妇女精品| 欧美福利精品| 亚洲小少妇裸体bbw| 在线午夜精品自拍| 国产欧美成人| 欧美国产精品人人做人人爱| 猛男gaygay欧美视频| 亚洲精品一区二区网址| 一本色道88久久加勒比精品| 国产精品卡一卡二| 久久久久久9| 欧美成人午夜影院| 亚洲男人影院| 久久精品国产清高在天天线| 亚洲国产岛国毛片在线| 亚洲美女中出| 国产日韩欧美高清| 欧美国产乱视频| 欧美性久久久| 欧美成人精品在线播放| 欧美精品国产| 欧美一区二区视频在线| 久久综合精品一区| 亚洲影音先锋| 蜜桃久久精品乱码一区二区| 亚洲一二三四区| 久久久久国色av免费观看性色| 亚洲日本欧美日韩高观看| 亚洲在线中文字幕| 亚洲国产黄色| 亚洲一区二区在线免费观看视频 | 久久久人成影片一区二区三区观看 | 99国内精品久久| 亚洲大胆美女视频| 亚洲精品一区中文| 国产亚洲电影| 亚洲欧洲日韩女同| 国产日产高清欧美一区二区三区| 欧美韩日一区| 国产区二精品视| 亚洲美女视频在线免费观看| 国产综合色一区二区三区| 日韩视频二区| 尤物yw午夜国产精品视频| 一区二区三欧美| 亚洲精品欧美精品| 久久午夜视频| 欧美专区亚洲专区| 欧美日韩在线综合| 亚洲国产精品一区二区www在线 | 亚洲人成网站在线观看播放| 午夜精品福利在线| 中文久久乱码一区二区| 另类成人小视频在线| 久久久国产午夜精品| 国产精品欧美久久久久无广告| 亚洲国产天堂久久综合| 在线精品在线| 久久久欧美精品sm网站| 久久免费精品日本久久中文字幕| 欧美日韩蜜桃| 亚洲老司机av| 一区二区三区久久精品| 蜜臀91精品一区二区三区| 久久欧美中文字幕| 国产一区二区精品丝袜| 欧美伊人久久| 久久久久亚洲综合| 好吊色欧美一区二区三区四区| 亚洲欧美制服另类日韩| 欧美中在线观看| 国产视频一区二区在线观看 | 久久综合中文| 欧美国产精品日韩| 亚洲国产高清视频| 免费不卡视频| 91久久久亚洲精品| 妖精视频成人观看www| 欧美日韩国产亚洲一区| 夜夜嗨av色综合久久久综合网| 亚洲夜间福利| 国产精品自拍三区| 久久精品国产第一区二区三区最新章节| 午夜一区不卡| 激情综合在线| 欧美激情影院| 亚洲一区二区三区久久 | 亚洲乱码国产乱码精品精 | 樱桃成人精品视频在线播放| 久久久久久久久久久成人| 欧美 日韩 国产在线| 亚洲美女91| 国产精品午夜av在线| 亚洲高清成人| 亚洲一区图片| 久久黄金**| 亚洲国产精品成人综合色在线婷婷| 久久久久久一区二区| 亚洲国产成人久久综合| 亚洲小说春色综合另类电影| 国产精品日韩二区| 久久精品一本| 亚洲精品少妇30p| 欧美一区综合| 亚洲国产欧美一区二区三区久久| 欧美激情区在线播放| 亚洲一区二区影院| 老司机67194精品线观看| 亚洲精品乱码久久久久久蜜桃麻豆 | 亚洲每日在线| 欧美一区亚洲| 亚洲精品美女91| 国产人成精品一区二区三| 欧美va亚洲va香蕉在线| 亚洲欧美激情四射在线日| 欧美顶级艳妇交换群宴| 性xx色xx综合久久久xx| 亚洲黄网站在线观看| 国产日韩av高清| 欧美日韩1080p| 久久影视三级福利片| 亚洲伊人网站| 日韩视频免费看| 亚洲国产精品久久久久| 久久久在线视频| 亚洲女女女同性video| 亚洲日本激情| 在线视频观看日韩| 国产亚洲精品激情久久| 欧美午夜激情视频| 欧美国产欧美综合| 老妇喷水一区二区三区| 久久精品在这里| 久久国产精品亚洲77777| 亚洲欧美日本精品| 亚洲在线视频免费观看|