Design C ++ APIs in the QT Style

Source: Internet
Author: User

A good article on API design, written by one of the founders of QT, is very enlightening. There are many problems in large-scale C ++ design. We can try to reduce these problems.

From: http://googollee.blog.163.com/blog/static/1159411200811321030894/

 

Design C ++ APIs in the QT Style

{
Function onclick ()
{
Neblog. gpermalinkpage. updownmenu. opensameclass ('fks _ 000000', 'translation ');
}
} "Href =" http://writeblog.csdn.net/# "> translation read 1210 comments 21 font size :{
Function onclick ()
{
Neblog. gpermalinkpage. updownmenu. changefont ('blogtext _ fks_084067085083088067085085087095085087084074080087087 ', 1); Return false;
}
} "Href =" http://writeblog.csdn.net/# "> large Large{
Function onclick ()
{
Neblog. gpermalinkpage. updownmenu. changefont ('blogtext _ fks_084067085083088067085085087095085087084074080087087 ', 2); Return false;
}
} "Href =" http://writeblog.csdn.net/# "> in Medium{
Function onclick ()
{
Neblog. gpermalinkpage. updownmenu. changefont ('blogtext _ fks_084067085083088067085085087095085087084074080087087 ', 3); Return false;
}
} "Href =" http://writeblog.csdn.net/# "> small SmallThis is an article officially published by QT and I think it is of great reference for designing APIs. So I used the translation by the way. The link in the original text does not work because the blog does not support the link. If you want an HTML file, directly ask me for it, or retrieve it here. (Advertisement by the way: toplanguage is a good discussion group. You are welcome to discuss your feelings, opinions, and usage about the language. The host is pongba)

The author Matthias ettrich, translator googol Lee, is here.

At trolltech, we have done a lot of research to improve the development experience of QT. In this article, I plan to share some of our findings and some of the principles we use when designing qt4, and demonstrate how to apply these principles to your code.

  • Six features of a good API
  • Convenience trap
  • Boolean parameter trap
  • Static Polymorphism
  • Name Art
  • Pointer or reference?
  • Example: qprogressbar
  • How to Design APIs

It is difficult to design application interfaces and APIs. This is an art that is as difficult as a design language. Too many principles can be selected here, and even many principles are in conflict with other principles.

Nowadays, computer science education puts a lot of effort on algorithms and data structures, but seldom focuses on the principles behind design languages and frameworks. This makes application programmers not ready to face more and more important tasks: creating reusable components.

Before the popularization of object-oriented languages, most of the reusable general code was written by the database provider, rather than the application programmer. In the world of QT, this situation has been significantly improved. At any time, using QT programming is to write new components. A typical QT application has at least several custom components that are repeatedly used in the program. In general, the same component will become part of other applications. The KDE and K desktop environments go further and use many append libraries to expand QT and implement hundreds of additional classes. (Generally, a class is a reusable component, which is not clearly written in the original article .)

But what makes a good and efficient C ++ API? Whether it is good or bad depends on many factors, such as the work at hand and the specific target group. Good APIs have many features that everyone wants, while others are for specific problem domains.

Six features of a good API

APIs are intended for programmers and are used to describe the GUI provided to end users. In the API, the P-band table Programmer (programmer), instead of the Program (Program), is used to emphasize that the API is used for programmers and for human programmers.

We firmly believe that the API should be minimal and complete, with clear and simple semantics, intuition, easy to remember, and guide people to write readable code.

  • Minimize:A minimal API is a class that has as few public members as possible and as few as possible. This principle makes the API easier to understand, better remember, and easier to debug and change.
  • Complete:A complete API is to provide all the desired functions. This may conflict with the minimization principle. In addition, if a member function is a class that should not belong to, many potential users will not find this function.
  • Clear and simple semantics:Like other design jobs, you must follow the principle of least surprise (the principle of least surprise ). Make common tasks simple and easy. Uncommon work is feasible, but it won't attract too much attention from users. When solving special problems, do not make the solution unnecessary to be overly generic. (For example, qmimesourcefactory in qt3 can call qimageloader to implement different APIs .)
  • Intuition:Just like other things on a computer, APIs must be intuitive. Different experiences and backgrounds may lead to different feelings when determining what is intuition and what is not. If an intermediate user does not read the document, he or she can use it (a semi-experienced user gets away without reading the documentation, but he does not know how to translate the get away command ), A programmer can understand the abbreviated code without understanding the API. This API is intuitive.
  • Easy to remember:The API is easy to remember and uses a unified and precise naming method. Use recognizable patterns and concepts and avoid abbreviations.
  • Lead to readable code ):Once the code is written, the code will be read (and deleted and modified) multiple times. Easy-to-Read code may take some time to write, but it can save other time in the product cycle.

Finally, remember that different types of users will use different parts of the API. Although simple instantiation of a QT class is very intuitive, it is reasonable for senior experts to read the document before trying to subclass it.

Convenience trap

This is a common misunderstanding: A Better API, with less code to do one thing. Always remember to write the code once and then read and understand it continuously. For example:

    QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
0, "volume");

It is far more difficult to read (or even write) than the following ):

    QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");

Boolean parameter trap

Boolean Parameters usually cause unreadable code. Furthermore, adding a Boolean parameter to an existing function is often an error. In QT, a traditional example is repaint (). This function carries a Boolean parameter to identify whether to erase the background (erased by default ). The code is usually written as follows:

    widget->repaint(false);

It is easy for beginners to understand this sentence as "Don't redraw "!

In this case, the Boolean parameter can reduce a function and avoid code expansion. In fact, this increases the amount of code. How many QT users really remember what the following three lines of programs do?

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

A better API may look like this:

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

In qt4, we re-designed widgets so that users no longer need to re-draw widgets without re-painting the background to solve this problem. Qt4 supports dual-Cache native and eliminates this feature.

Here are some examples:

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

An obvious solution is to replace the Boolean parameter with the enumeration type. This is exactly how we handle qstring case sensitivity in qt4. Comparison:

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

Static Polymorphism

Similar classes should contain similar APIs. When necessary -- that is, when Runtime polymorphism is required -- this can be achieved through inheritance. However, polymorphism still occurs during the design period. For example, if you use qlistbox instead of qcombobox, or use qslider instead of qspinbox, you will find that similar APIs make this replacement very easy. This is what we call static polymorphism ".

Static polymorphism also makes API and program patterns easier to remember. As a conclusion, a group of related classes use similar APIs, sometimes better than providing a perfect separate API for each class.

The concept to be introduced by C ++ 0x is the syntax layer Implementation of static polymorphism. This is more powerful and easy to use than a separate function name .)

Name Art

Naming is probably the only and most important issue during API design. What should I call this class? What should a member function be called?

General naming rules

Some rules are usually useful for all names. First, as I mentioned earlier, do not use abbreviations. Obviously, for example, "Prev" indicates "previous" is not cost-effective in the long term, because users must remember which words are abbreviations.

If the API itself is inconsistent, things will naturally become very bad. For example, qt3 has activatepreviouswindow () and fetchprev (). It is easier to create consistent APIS by sticking to the "no abbreviation" rule.

Another important but more subtle rule is that when designing a class, you must try your best to ensure that the sub-class namespace is clean. In qt3, this rule is not followed well. For example, use qtoolbutton. What do you want to do if you call name (), caption (), text (), or textlabel () for a qtoolbutton in qt3? You can try qtoolbutton in QT designer:

  • The Name property is inherited from the qobject, indicating the internal name of an object for debugging and testing.
  • The Caption property is inherited from the qwidget, indicating the title of the window. This title is visually meaningless to the qtoolbutton because it is always created along with the parent window.
  • The text property is inherited from the qbutton. Generally, it is the actual text on the button, unless usetextlabel is true.
  • Textlabel is declared in qtoolbutton and displayed on the button when usetextlabel is true.

Due to readability concerns, name is called objectname in qt4. caption is changed to windowstitle, and there is no separate textlabel attribute in qtoolbutton.

Name a class

Identifies a group of classes rather than finding an appropriate name for each class. For example, all the view classes (model-aware item view classes) of the pattern Sensing Project in qt4 have the suffix of-view (qlistview, qtableview, and qtreeview ), the corresponding project-based classes are replaced by extensions-widgets (qlistwidget, qtablewidget, and qtreewidget ).

Name enumeration types and their values

When declaring enumeration, remember that in C ++ (unlike Java and C #), using enumeration values does not require type information. The following example demonstrates the harm caused by a too common name for the enumerated value:

    namespace Qt
{
enum Corner { TopLeft, BottomRight, ... };
enum CaseSensitivity { Insensitive, Sensitive };
...
};

tabWidget->setCornerWidget(widget, Qt::TopLeft);

str.indexOf("$(QTDIR)", Qt::Insensitive);

In the last line, what does insensitive mean? One guiding ideology for naming enumeration values is to repeat at least one element of an enumeration type name in each enumeration value:

    namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, ... };
enum CaseSensitivity { CaseInsensitive,
CaseSensitive };
...
};

tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
str.indexOf("$(QTDIR)", Qt::CaseInsensitive);

When enumeration values can be connected as a sign, the traditional method is to save the result of "or" as an int, which is not type-safe. Qt4 provides a template class qflags <t> to implement type security, where T is an enumeration type. For ease of use, QT provides typedef for many flag class names, so you can use the QT: Alignment type to replace qflags <QT: alignmentflag>.

For convenience, we give the name of the singular Enumeration type (which indicates that only one flag can be used for an enumeration value at a time), while the "flag" uses the plural name. For example:

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

In some cases, the "flag" class uses the singular name. In this case, the enumeration class uses-flag as the Suffix:

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

(Why not use the-flag as the suffix for the "flag" class, but the enumerated value as the suffix? I feel a little confused ......)

Name functions and Parameters

A rule for naming a function is to explicitly indicate whether the function has any side effects. In qt3, the constant function qstring: simplifywhitespace () violates this principle because it returns a qstring instance of the class, rather than as prompted by the name, changed the instance that calls this function. In qt4, this function is renamed as qstring: simplified ().

Parameter names are important information sources for programmers, although they are not directly displayed in code when using APIs. Because modern IDE can automatically display parameter names (that is, functions such as automatic sensing or automatic completion) When programmers write code, it is worth the time to give a proper name to the parameters declared in the header file, the same name is also used in the document.

Set the setter, Getter, and attribute names for boolean values.

It is always very painful to set a function and extract a proper name for a Boolean attribute. Should the extraction function be called checked () or ischecked ()? Scrollbarsenabled () or arescrollbarenabled ()?

In qt4, we use the following naming rules to extract functions:

  • The is-prefix is used to describe the attributes of a class. For example:

    • Ischecked ()
    • Isdown ()
    • Isempty ()
    • Ismovingenable ()

    In addition, the descriptive class attributes applied to plural nouns do not have a prefix:

    • Scrollbarsenabled ()InsteadArescrollbarsenabled ()
  • The attributes of the verb class do not use the prefix and do not use the third person (-S ):
    • Acceptdrops ()InsteadAcceptsdrops ()
    • Allcolumnsshowfocus ()
  • The attributes of a noun class. Generally, there is no prefix:
    • Autocompletion ()InsteadIsautocompletion ()
    • Boundarychecking ()

    Sometimes, the absence of a prefix may cause misunderstanding. In this case, the prefix is -:

    • Isopenglavailable ()InsteadOpenGL ()
    • Isdialog ()InsteadDialog ()

    (By calling the dialogue () method, a qdigue * instance is expected to be returned normally .)

Set the function name to inherit from the extracted function name. It only removes all prefixes and uses set-as the prefix, for example, setdown () and setscrollbarsenabled (). The attribute name is the same as that of the extract function, but the prefix is removed.

Pointer or reference?

What is the best choice for outgoing parameters, pointer or reference?

    void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const

Most C ++ books recommend that you use references when they can be referenced. This is because references are generally considered to be "safer and easier to use" than pointers ". However, at trolltech, we tend to use pointers because it makes the code easier to read. Comparison:

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

Only the first line can clearly explain that after the function is called, H, S, and V are likely to be modified.

Example: qprogressbar

To demonstrate how to actually apply these concepts, we will learn the qprogressbar in qt3 and compare it with the APIs that are connected to qt4 Li. In qt3:

    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);
...
};

APIs are complex and not uniform. For example, the function of settotalsteps () and setprogress () are tightly coupled.

The key to improving the API is to note that qprogressbar and qt4 qabstractspinbox classes and their subclasses qspinbox, qslider and qdial are very similar. Solution? Replace progress and totalsteps with minimum, maximum, and value. Add the aluechanged () signal. Add the setrange () function.

Then observe that progressstring, percentage, and indicator actually refer to the text displayed on the progress bar. In general, the text is the percentage information, but you can also use setindicator () to set it to any character. The following is a new API:

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

The default text information is the percentage information. Text information can be changed by re-implementing text.

In qt3 API, setcenterindicator () and setindicatorfollowstyle () are two functions that affect alignment. They can be conveniently implemented by a function, setalignment ():

    void setAlignment(Qt::Alignment alignment);

If the programmer does not call setalignment (), the alignment is based on the current style. For Motif-based styles, text is displayed in the center; for other styles, text is displayed on the right.

This is the improved qprogressbar API:

    class QProgressBar : public QWidget
{
...
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);
...
};

How to Design the API (the original article is how to get APIs right. I always want to be we do APIs right ......)

Quality assurance is required for APIs. The first revision cannot be correct; you must perform a test. Write some use cases: Check the code that uses these APIs and verify that the code is easy to read.

Other tips include getting others to use these Apis when there are documents and no documents, or writing documents for the API class (including class overview and independent functions ).

When you get stuck, writing a document is also a way to get a good name: just try to put entries (classes, functions, enumerated values, and so on) write down and use the first sentence you wrote as inspiration. If you cannot find a precise name, it usually means that this entry should not exist. If all the previous things fail and you confirm the existence of this concept, create a new name. After all, the "widgets", "Event", "Focus", and "buddy" names are like this.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.