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

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 閱讀(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>
            尤物99国产成人精品视频| 欧美国产三区| 亚洲一区欧美激情| 亚洲日本久久| 猛干欧美女孩| 欧美成人午夜激情在线| 欧美丰满高潮xxxx喷水动漫| 亚洲成在人线av| 亚洲每日更新| 亚洲欧美视频一区| 久久久久久电影| 欧美激情免费在线| 国产精品午夜电影| 99re66热这里只有精品4| 日韩一区二区免费高清| 午夜一区不卡| 免费亚洲一区| 欧美经典一区二区三区| 99re8这里有精品热视频免费| 日韩天堂av| 欧美一区观看| 欧美精品亚洲精品| 国产一区亚洲| 亚洲视频1区| 猫咪成人在线观看| 一区二区三区 在线观看视频| 久久se精品一区精品二区| 欧美精品亚洲一区二区在线播放| 国产精品亚洲人在线观看| 亚洲国产第一| 久久九九精品99国产精品| 亚洲免费av网站| 久久免费视频一区| 国产美女精品视频| 亚洲综合日本| 91久久精品国产91久久性色| 欧美伊人精品成人久久综合97 | 日韩视频在线一区二区| 久久精品二区亚洲w码| 亚洲日本在线视频观看| 久久蜜桃精品| 国产曰批免费观看久久久| 亚洲午夜极品| 亚洲精品免费在线观看| 巨乳诱惑日韩免费av| 国产美女一区二区| 欧美揉bbbbb揉bbbbb| 狠狠久久亚洲欧美| 性视频1819p久久| 一区二区国产在线观看| 欧美大片18| 亚洲高清自拍| 欧美11—12娇小xxxx| 久久精品二区三区| 国产丝袜一区二区| 欧美一二三区精品| 亚洲摸下面视频| 欧美性色综合| 亚洲综合二区| 亚洲一级高清| 国产精品综合不卡av| 亚洲欧美日韩国产另类专区| 99国产精品自拍| 国产精品电影观看| 午夜精品一区二区三区在线| 亚洲伊人一本大道中文字幕| 欧美午夜不卡| 欧美在线免费观看视频| 欧美在线播放| 亚洲欧美日本日韩| 日韩视频中文| 欧美国产第二页| 久久综合综合久久综合| 亚洲国产精品va在线看黑人 | 女人色偷偷aa久久天堂| 在线观看一区欧美| 亚洲福利视频网站| 欧美人与禽猛交乱配| 99精品视频免费在线观看| 中国av一区| 激情久久久久久久| 亚洲国产精品福利| 国产精品国产自产拍高清av王其| 欧美一区2区视频在线观看| 欧美在线观看视频在线 | 国产日产高清欧美一区二区三区| 欧美一区二区精品| 久久夜色撩人精品| 亚洲图片在线| 久久精品国产免费看久久精品| 亚洲电影免费观看高清完整版在线观看| 欧美韩日一区二区| 国产精品伦理| 欧美高清视频免费观看| 欧美午夜欧美| 亚洲精品中文字幕在线| 亚洲午夜性刺激影院| 在线播放亚洲一区| 在线视频一区二区| 亚洲国产日韩欧美在线99| 99精品视频免费| 1024亚洲| 午夜久久美女| 日韩一区二区精品视频| 午夜欧美视频| 一区二区三区免费网站| 久久久久一本一区二区青青蜜月| 中文av一区二区| 久热精品视频在线免费观看| 午夜在线一区| 欧美日韩国产专区| 欧美成人高清| 国产一级一区二区| 一区二区三区你懂的| 亚洲国产精品久久久久婷婷老年 | av72成人在线| 久久精品亚洲一区二区三区浴池| 亚洲视频网在线直播| 欧美.日韩.国产.一区.二区| 欧美一区二区三区免费观看| 欧美人牲a欧美精品| 欧美国产日韩一二三区| 日韩午夜激情| 欧美二区在线| 精品成人一区二区| 欧美一级久久久| 亚洲综合色网站| 欧美日韩妖精视频| 亚洲精选大片| 一本久久综合亚洲鲁鲁五月天| 久久综合中文| 欧美成人久久| 亚洲国产另类久久久精品极度| 久久精品夜色噜噜亚洲a∨| 久久久国产午夜精品| 国产精品一区二区三区久久| 一本色道久久综合亚洲精品小说| 亚洲精品在线视频| 欧美国产一区二区在线观看| 欧美成人亚洲| 激情视频亚洲| 久久综合伊人| 男女激情久久| 亚洲国产小视频| 欧美高清不卡| 91久久夜色精品国产网站| 亚洲国内自拍| 免费看av成人| 欧美亚洲系列| 香蕉久久夜色精品国产使用方法| 亚洲一区二区三区精品动漫| 欧美日韩成人在线| 亚洲精品中文在线| 午夜激情综合网| 国内精品写真在线观看| 久久综合久久久| 99精品国产99久久久久久福利| 先锋影音国产精品| 国产综合久久久久久| 欧美成人69| 亚洲午夜成aⅴ人片| 玖玖玖国产精品| 一本色道久久综合亚洲精品不| 国产精品视频yy9099| 久久九九热免费视频| 最新日韩av| 久久大逼视频| 99精品欧美一区二区蜜桃免费| 国产精品嫩草99av在线| 久久香蕉国产线看观看av| 亚洲国产成人久久综合| 亚洲欧美日本伦理| 亚洲日本中文字幕区| 国产精品女主播| 欧美电影在线免费观看网站| 亚洲一区黄色| 亚洲精品乱码视频| 看欧美日韩国产| 亚洲欧美影音先锋| 亚洲乱码国产乱码精品精可以看 | 久久久久久久久久久久久女国产乱| 一区二区在线视频| 国产精品久久久亚洲一区| 六月婷婷一区| 西西裸体人体做爰大胆久久久| 91久久精品www人人做人人爽| 久久久国产亚洲精品| 一区二区三区高清不卡| 激情综合五月天| 国产伦精品一区二区三区照片91| 欧美电影打屁股sp| 黄色亚洲大片免费在线观看| 亚洲免费黄色| 欧美成人四级电影| 欧美一区二区三区四区在线观看地址| 91久久久久久久久久久久久| 国产有码一区二区| 国产伦一区二区三区色一情| 欧美日韩国产精品一卡| 麻豆久久精品|