C++ 的API 设计指导

本文详述了Qt官网上的C++ API设计准则,包括好API的六个特征、静态多态、基于特性的API、C++规范、API语义与文档、命名艺术及如何避免常见陷阱。API设计应考虑程序员的使用体验,注重简洁性、完整性、语义清晰、直觉性和可读性。此外,文章还讨论了指针与引用、常量、返回值等方面的最佳实践,强调了命名的重要性。
摘要由CSDN通过智能技术生成

http://qt-project.org/wiki/API-Design-Principles

摘要:    

    此文为Qt 官网上的API设计(for C++)指导准则,其中有不少原则具有普遍适用性,整个篇幅中有很多示例,是Qt在API设计上的实践。   



正文:  

    Qt 一致、易掌握、强大的API是它的众多著名的优点之一。此文总结了我们在设计Qt风格的API所积累的做法。其中许多准则是通用的;而其他内容更偏向与约定,遵守它主要是为了与已有的API保持一致。    

    虽然这些准则主要用于公共API, 你也可以在设计私有API时使用它们,把它作为与其他开发者之间的约定。


1.好API的6个特点(Six Characteristics of Good API)

    API对与程序员的关系就如同GUI与用户的关系。API中的’P’实际上指的是’程序员’,而不是’程序’,它强调了所有API都是由程序员使用的事实。   

    Matthias 在他的Qt Quarterly 13 article about API design 中说他相信API应该很少并且是完整的,有清晰简单的语义,可凭直觉知道的,容易记忆而且能产生可读的代码。


    数量最小化(Be minimal)

    数量最小化的API拥有尽可能少的公有类,每个公有类的公有成员也很少。依次可使理解,记忆,调试,改变API更容易。


    功能完整(Be complete)

    完整的API包含了所有需要用到的功能。这和API的数量最小化有点冲突。另外,如果某个成员函数放到了错误的类里,需要使用此方法的用户就会找不到它。


   清晰简单的语义(Have clear and simple semantics)

    如同其他的设计,我们应该遵守意外最少的原则(principle of least surprise),把常用的操作变得容易,很少用的操作不应该很显眼。不要把没必要通用的解决方案普遍化。例如,在Qt 3 中,QMimeSourceFactory 不应被命名为 QImageLoader。


    直觉性强(Be intuitive)

    API 应该有较强的直觉性,就像计算机里的其他内容,。不同的履历和背景导致了不同人有不同直觉。如果一个经验不很丰富的用户没有阅读文档就能搞懂某个API,明白此API构成的代码,说明此API直觉性较强。


   容易记忆( Be easy to memorize)

    为使API易于记忆,应选择一个具有一致性和精确性的命名约定。使用易于识别的模式和概念,避免使用缩写。


    能写出可读代码(Lead to readable code)

     代码只写一次,但是却被浏览,调试,改变很多次。可读性好的代码可能需要更长的时间来写,却能在产品的整个生命周期中节省时间。

    最后,记住不同的用户使用不同部分的API。尽管记住简单得使用一个Qt类的示例更直接,最好还是告诉用户在使用API时阅读相关一下文档。


2.静态多态(Static Polymorphism)

    相似的类应该有相似的API。运行时多态通过继承体系实现。然而多态也发生在设计阶段。例如,如果你用QProgressBar替换QSlider,或者是QString替换QByteArray,你会发现API的简洁性使替换如此容易。此所谓“静态多态”。

    静态多态也使记忆API和编程模式更加容易。因此,一组有相似接口的相关类比每个类都有自己的一套接口更好。

    总体上讲,在Qt中,比起没有强有力的理由实现的继承关系,我们跟喜欢依赖静态多态。这种做法可确保公有类较少,使刚学习Qt的用户认清路线。


   好的静态多态

    QDialogButtonBox与 QMessageBox 在按钮操作(addButton(), setStandardButtons()等等 )有相似的API,而没有继承某个”QAbstractButtonBox”的类。


    糟糕的静态多态

    QTcpSocket 与 QUdpSocket 都继承了 QAbstractSocket ,它们是两个行为非常不同的类。似乎没有什么人曾经或会去使用一个QAbstractSocket指针。


   不能确定的静态多态

    QBoxLayout 是 QHBoxLayout 与 QVBoxLayout 的基类,好处:可以在工具栏中使用QBoxLayout调用setOrientation() 使其变为水平/垂直。坏处:需要一个额外的类,并且有可能导致用户写出这样没什么意义的代码,((QBoxLayout *)hbox)->setOrientation(Qt::Vertical)。


 3. 基于特性的API(Property-Based APIs )

    新的Qt类倾向于基于特性的API,例如:

[cpp]  view plain  copy
  1. QTimer timer;  
  2. timer.setInterval(1000);  
  3. timer.setSingleShot(true);  
  4. timer.start();  

    特性,是指此对象的任何属性——无论它是否是Q_PROPERTY。在实践中,用户应能够以任何顺序设置这些特性,也就是说,这些特性应该是正交的。例如,之前的代码可以写成:

[cpp]  view plain  copy
  1. QTimer timer;  
  2. timer.setSingleShot(true);  
  3. timer.setInterval(1000);  
  4. timer.start();  

    【译者注:正交特性:改变某个特性而不会影响到其他的特性。《程序员修炼之道》中讲了一个关于正交性的直升飞机坠毁的例子。】

    方便起见,也写成:

[cpp]  view plain  copy
  1. timer.start(1000);  

    类似地,对于QRegExp,

[html]  view plain  copy
  1. QRegExp regExp;  
  2. regExp.setCaseSensitive(Qt::CaseInsensitive);  
  3. regExp.setPattern("***.*");  
  4. regExp.setPatternSyntax(Qt::WildcardSyntax);  

    为实现这种类型的API,最好不要过早构建目标。例如,在QRegExp的例子中,在不知道模式语法的时候,在setPattern()中编译"***.*"为时过早。

    各种特性经常关联在一起。在上面的例子中,我们必须小心。思考一下当前风格提供的“默认的图标尺寸”和QToolButton的”iconSize”属性:

[cpp]  view plain  copy
  1. toolButton->iconSize();    // returns the default for the current style  
  2.  toolButton->setStyle(otherStyle);  
  3.  toolButton->iconSize();    // returns the default for otherStyle  
  4.  toolButton->setIconSize(QSize(52, 52));  
  5.  toolButton->iconSize();    // returns (52, 52)  
  6.  toolButton->setStyle(yetAnotherStyle);  
  7.  toolButton->iconSize();    // returns (52, 52)  

    需要提醒的是,一旦设置了 iconSize,它就一直保持设置状态;改变当前的风格不会改变此事。这样很好。有时候,重置某个特性也很有用,有两种方法:

       (1)传入一个特殊值 (如 QSize(), -1, 或者 Qt::Alignment(0)) ,意味着重置     

       (2)提供一个明确的重置接口,如 resetFoo() 和 unsetFoo() 对于iconSize,使用Qsize()就足够了。

    在某些情况下,getters 返回的结果与设置的可能不同。例如,如果调用widget->setEnabled(true),如果它的parent处于disabled状态,widget->isEnabled()仍然返回 false。这样是可行的,正是我们想要的(widget的父widget处于disabled状态,此widget也应该变为灰色,就好象也处于disabled状态一样,但是它会记得,其本身并没有处于disabled状态,正等待它的父widget变为enabled.),但是诸如这样的特性必须被详细列入文档。


4.C++ API 规范(C++ Specifics)

    指针与引用(Pointers vs. References)

最好的输出参数的类型是什么,指针还是引用?

[cpp]  view plain  copy
  1.    void getHsv(int *h, int *s, int *v) const  
  2.    void getHsv(int &h, int &s, int &v) const  
    大多数C++书籍基于引用比指针“安全和优雅”的观点,推荐尽可能使用引用。相比之下,我们在开发Qt时更喜欢指针,因为使用指针可使用户代码可读性更好。比较下面两个例子:

[cpp]  view plain  copy
  1.  color.getHsv(&h, &s, &v);  
  2.  color.getHsv(h, s, v);  
    仅有第一行代码充分显示h,s,v 很可能被此函数调用修改。
    虚函数(Virtual Functions)

    类的成员函数声明为virtual,一般是为了通过其子类实现此函数来定制函数的行为。将函数声明为virtual的目的是为了实现动态多态。如果在类外面没有人调用声明为virtual的函数,将其声明为virtual之前,你应该多加小心。

[cpp]  view plain  copy
  1. // QTextEdit in Qt 3: member functions that have no reason for being virtual  
  2.   virtual void resetFormat();  
  3.   virtual void setUndoDepth( int d );  
  4.   virtual void setFormat( QTextFormat *f, int flags );  
  5.   virtual void ensureCursorVisible();  
  6.   virtual void placeCursor( const QPoint &pos;, QTextCursor **c = 0 );  
  7.   virtual void moveCursor( CursorAction action, bool select );  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值