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

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容易記憶,使用一致且精準的命名規范.使用容易識別的模式和概念,避免使用縮寫.
  • 能生成可讀生代碼:代碼只寫一遍,卻要閱讀許多遍(調試或更改).可讀性的代碼有時候可能需要多敲些字,但是從產品生命周期中可節省很多時間.

最后,請記住:不同的用戶使用API的不同部分.當簡單地使用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 閱讀(7857) 評論(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  回復  更多評論
  

<2008年5月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

常用鏈接

留言簿(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>
            欧美日韩中文字幕在线| 亚洲私人影院| 欧美一区三区三区高中清蜜桃 | 亚洲专区一二三| 亚洲国产mv| 免费视频最近日韩| 欧美激情精品久久久久久黑人| 国产精品久久久久天堂| 欧美四级在线| 国产亚洲精品高潮| 亚洲高清视频在线| 亚洲一区免费网站| 亚洲欧美一区二区原创| 久久精品一本久久99精品| 欧美午夜精品理论片a级大开眼界 欧美午夜精品理论片a级按摩 | 亚洲欧美日韩综合| 亚洲视频精品| 久久九九精品99国产精品| 麻豆精品在线播放| av成人天堂| 久久狠狠一本精品综合网| 老司机午夜精品| 欧美日韩一卡二卡| 国内精品久久久久影院薰衣草| 欧美岛国激情| 毛片基地黄久久久久久天堂 | 午夜精品久久久久99热蜜桃导演| 国产一区视频网站| 亚洲国产aⅴ天堂久久| 亚洲一区二区久久| 久久综合五月| 亚洲看片免费| 久久精品视频免费播放| 国产精品国产福利国产秒拍| 韩国欧美一区| 亚洲欧美日韩精品久久亚洲区 | 日韩网站免费观看| 欧美一区二区三区电影在线观看 | 久久久精品动漫| 欧美三级第一页| 亚洲国产二区| 久久久精品国产免费观看同学| 亚洲免费影视第一页| 麻豆freexxxx性91精品| 一区二区三区蜜桃网| 欧美国产免费| 最新日韩欧美| 欧美国产日本在线| 久久精品亚洲| 国产欧美精品一区二区三区介绍| 国产欧美亚洲视频| 亚洲一区日本| 一区二区三区欧美激情| 欧美/亚洲一区| 亚洲高清自拍| 欧美成人精品三级在线观看| 欧美一二三视频| 国产视频精品免费播放| 欧美一区二区三区免费视频| 一区二区三区高清在线观看| 欧美三日本三级少妇三99| 99在线精品免费视频九九视| 亚洲经典在线| 亚洲欧洲精品一区二区精品久久久| 娇妻被交换粗又大又硬视频欧美| 国产精品久久看| 亚洲九九精品| 亚洲人成高清| 欧美日本在线观看| 亚洲视频日本| 先锋a资源在线看亚洲| 国产乱理伦片在线观看夜一区| 国产日韩精品一区二区| 亚洲一区国产一区| 亚洲综合首页| 国产女人精品视频| 久久久久青草大香线综合精品| 亚洲国产欧美一区二区三区久久| 亚洲免费激情| 欧美天堂亚洲电影院在线播放| 国产午夜精品全部视频在线播放| 国内成人在线| 蜜臀av在线播放一区二区三区| 91久久久国产精品| 欧美日韩国产经典色站一区二区三区 | 久久亚洲精品视频| 久热精品在线视频| 亚洲精选国产| 一区二区三区四区五区在线| 国产偷国产偷精品高清尤物| 欧美aaa级| 欧美性色aⅴ视频一区日韩精品| 伊人成人在线| 亚洲经典三级| 国产一区清纯| 亚洲精品黄色| 国产美女精品在线| 欧美激情无毛| 国产美女精品免费电影| 欧美激情免费观看| 国产精品日本| 亚洲国产精品久久精品怡红院| 欧美伊人精品成人久久综合97| 欧美成人福利视频| 亚洲欧美激情在线视频| 久久免费国产| 午夜亚洲一区| 欧美区在线观看| 欧美成人午夜影院| 国产精品日韩欧美综合| 牛牛精品成人免费视频| 国产精品女人毛片| 欧美成人日韩| 国产三区二区一区久久| 亚洲免费精品| 亚洲精品乱码久久久久| 欧美中文在线视频| 午夜精品久久久久久久久久久久久| 最新日韩av| 一区二区三区无毛| 亚洲免费视频一区二区| 亚洲精品视频在线看| 欧美一区二区久久久| 免费在线亚洲| 久久亚洲私人国产精品va媚药| 亚洲激情第一页| 国产在线观看91精品一区| 亚洲日本成人| 亚洲精品欧美在线| 免费在线日韩av| 欧美二区在线| 亚洲人成人99网站| 欧美不卡高清| 亚洲激情啪啪| 99爱精品视频| 国产精品成人免费| 这里只有精品视频| 亚洲欧美日本精品| 国产精品揄拍一区二区| 亚洲欧美日韩国产中文| 欧美在线观看你懂的| 国产视频一区二区在线观看| 欧美一区成人| 欧美国产精品久久| 99精品视频免费观看| 欧美第一黄网免费网站| 91久久精品www人人做人人爽| 国产精品成人aaaaa网站| 亚洲国内高清视频| 一区二区欧美在线| 国产精品香蕉在线观看| 午夜精品一区二区三区电影天堂| 在线免费观看欧美| 麻豆91精品91久久久的内涵| 91久久精品国产91久久性色| 亚洲综合国产| 国内一区二区三区| 欧美成人午夜| 亚洲一区欧美| 老司机免费视频一区二区| 亚洲人成亚洲人成在线观看图片| 99视频+国产日韩欧美| 亚洲一区二区三区乱码aⅴ蜜桃女 亚洲一区二区三区乱码aⅴ | 亚洲激情电影中文字幕| 一本色道88久久加勒比精品| 国产精品久久久久久亚洲毛片 | 久色婷婷小香蕉久久| 在线视频成人| 欧美日本高清| 欧美一区二视频| 亚洲第一福利视频| 亚洲欧美视频一区| 亚洲国产成人午夜在线一区| 欧美视频一区| 久久精品国产欧美亚洲人人爽| 日韩亚洲欧美高清| 国产精品国产三级国产普通话99| 欧美激情小视频| 亚洲欧美影院| 最近看过的日韩成人| 欧美日本不卡| 久久亚洲欧美| 亚洲在线播放| 亚洲欧洲精品一区二区三区波多野1战4 | 久久影院午夜论| 亚洲欧美日本国产专区一区| 久久综合九色综合欧美就去吻| 国产精品久久久久久影院8一贰佰| 久久精品国语| 亚洲高清资源| 久久影视三级福利片| 在线中文字幕一区| 欧美超级免费视 在线| 亚洲欧美日韩国产成人精品影院| 欧美美女bbbb| 久久久精品国产免费观看同学| 新67194成人永久网站| 亚洲高清免费视频| 国产一区二区三区的电影| 欧美性猛片xxxx免费看久爱 |