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

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  回復  更多評論
  

<2009年9月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

常用鏈接

留言簿(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精品99久久久久久宅男| 亚洲亚洲精品三区日韩精品在线视频 | 亚洲午夜精品久久久久久app| 久久久久国产精品www| 一区二区三区成人| 欧美麻豆久久久久久中文| 一色屋精品视频在线看| 久久精品女人| 欧美一区二区三区久久精品| 国产精品久久久久久久久久久久久久| 日韩亚洲欧美一区| 亚洲精品欧美极品| 欧美日韩性生活视频| 日韩午夜剧场| 亚洲裸体俱乐部裸体舞表演av| 欧美xx69| 亚洲一二三区在线| 99这里只有精品| 国产精品高潮呻吟久久av无限 | 亚洲色无码播放| 一级成人国产| 国产日韩欧美不卡在线| 久久精品国产欧美激情| 翔田千里一区二区| 性欧美精品高清| 狠狠干成人综合网| 欧美激情在线免费观看| 欧美激情bt| 一区二区免费看| 国产精品99久久久久久www| 国产精自产拍久久久久久| 久久国产主播精品| 美脚丝袜一区二区三区在线观看 | 久久一区中文字幕| 欧美xx69| 性欧美长视频| 久久人人97超碰精品888| 亚洲精选在线观看| 欧美在线视频在线播放完整版免费观看| 午夜免费在线观看精品视频| 在线观看亚洲a| 亚洲毛片在线看| 国产一区二区欧美| 亚洲国产三级在线| 国产精品久久77777| 老色鬼精品视频在线观看播放| 欧美韩日高清| 久久国产精彩视频| 欧美风情在线观看| 亚洲欧美日韩第一区| 老司机午夜精品视频| 夜夜嗨网站十八久久| 欧美在线高清视频| 一区二区日韩免费看| 久久精品免费观看| 亚洲视频在线观看三级| 久久久久九九视频| 亚洲永久精品大片| 久久久久久**毛片大全| 午夜精品一区二区三区在线| 久久免费视频这里只有精品| 亚洲午夜视频| 免播放器亚洲一区| 久久这里只有| 国产欧美三级| 99视频国产精品免费观看| 在线观看中文字幕不卡| 亚洲影院一区| 中文av一区特黄| 另类av导航| 久久在线视频在线| 国产欧美精品一区| 在线综合视频| 一区二区欧美在线| 欧美成人在线免费观看| 久久综合综合久久综合| 国产精品久久毛片a| 日韩午夜在线播放| 日韩视频在线你懂得| 免费试看一区| 欧美xxxx在线观看| 1204国产成人精品视频| 羞羞答答国产精品www一本| 亚洲一区图片| 欧美视频精品一区| 亚洲欧洲一区二区三区| 亚洲精品黄色| 日韩亚洲欧美一区| 亚洲精品一区二区三区婷婷月| 久久天堂精品| 欧美承认网站| 国产一区二区你懂的| 一区二区免费在线播放| 欧美国产日本在线| 亚洲国内欧美| 99re6这里只有精品| 欧美日韩第一页| 99热免费精品在线观看| 亚洲免费在线精品一区| 欧美视频亚洲视频| 亚洲一区二区视频在线| 香蕉av777xxx色综合一区| 国产精品尤物| 久久久91精品国产一区二区精品| 久久国产婷婷国产香蕉| 揄拍成人国产精品视频| 久久综合图片| 日韩亚洲欧美高清| 欧美一级成年大片在线观看| 国产午夜亚洲精品不卡| 欧美一区二区在线| 女女同性精品视频| 99热免费精品| 国产精品r级在线| 欧美影院精品一区| 欧美二区在线观看| 亚洲在线中文字幕| 国产一区二区三区四区五区美女| 久久久久久久久蜜桃| 亚洲成人在线视频网站| 中日韩美女免费视频网站在线观看| 欧美午夜视频一区二区| 久久精品人人爽| 亚洲国产欧美一区二区三区久久 | 久久xxxx精品视频| 欧美黄色成人网| 性8sex亚洲区入口| 影音欧美亚洲| 欧美日韩在线播放三区四区| 午夜精品一区二区三区在线视| 麻豆九一精品爱看视频在线观看免费| 1769国产精品| 欧美午夜免费影院| 久久先锋影音| 在线亚洲观看| 亚洲国产精品va在线看黑人| 亚洲在线免费视频| 亚洲国产精品小视频| 欧美日韩一区二区在线| 欧美在线影院| 99视频精品免费观看| 麻豆九一精品爱看视频在线观看免费| 一级成人国产| 亚洲第一福利社区| 国产精品久久久久久妇女6080| 久久欧美中文字幕| 亚洲欧美日韩精品久久| 亚洲日本无吗高清不卡| 久久人91精品久久久久久不卡| 一本色道久久综合亚洲二区三区 | 亚洲欧美三级伦理| 亚洲人成人77777线观看| 国产精品一区=区| 欧美精品在线视频| 亚洲激情视频在线观看| 久久本道综合色狠狠五月| av成人动漫| 亚洲电影专区| 影音先锋亚洲视频| 国产伦精品一区二区三区照片91| 欧美日韩不卡合集视频| 另类av导航| 美女日韩在线中文字幕| 性欧美1819sex性高清| 亚洲综合丁香| 一本一本久久a久久精品综合妖精| 欧美激情1区2区3区| 久久视频在线看| 久久国产乱子精品免费女 | 欧美高清一区二区| 久热国产精品| 久久精品麻豆| 欧美在线观看天堂一区二区三区 | 亚洲调教视频在线观看| 99国产成+人+综合+亚洲欧美| 欧美激情一区二区三区高清视频| 裸体丰满少妇做受久久99精品| 欧美一区二区视频在线| 午夜亚洲福利| 欧美一级大片在线观看| 日韩亚洲成人av在线| 中日韩美女免费视频网站在线观看 | 亚洲美女区一区| 亚洲七七久久综合桃花剧情介绍| 在线看欧美日韩| 在线成人免费观看| 在线电影国产精品| 亚洲欧洲一区二区三区| 亚洲精品1区2区| 一本大道久久a久久精二百| 99精品99| 欧美诱惑福利视频| 久久久久久香蕉网| 欧美成va人片在线观看| 欧美激情亚洲综合一区| 亚洲精品影视在线观看| 亚洲图片自拍偷拍| 久久久久久黄| 欧美精品首页| 国产精自产拍久久久久久|