On the design and implementation of API

Source: Internet
Author: User

http://blog.csdn.net/horkychen/article/details/46612899

The design of API is a unique field in software development. The main special point is that the API is for developers to use the interface, that is, application Programmer Interfaces. Similar to the role of a GUI that users can use directly. Therefore, it is more important to consider the user's "experience" relative to the principle of software design.

Many well-known tools and library authors have written related works, detailing their design and implementation points in the API. The following discussion is from these predecessors of the work of the results summed up. The following is the first list of resources:

    • 1. The art of Software framework design (Jaroslav Tulach, NetBeans)
    • 2.Little Manual of API Design (Jasmin blanchete, Qt)
    • 3.Preserving backward compatibility (Garrett Rooney, Subversion)
    • 4.API Design Principles (Qt Wiki)
    • 5.How to design a good APIs and why it Matters (Google)
About API

The narrow sense API may simply be a dynamic library (shared library) that provides the functionality of the interface definition. Broadly, the API is divided into the public API and the internal API. There is an interface to the external output of the overall software system (including the interface to communicate with the device), as well as an interface definition provided to the upper module by an underlying module in the system.

The seemingly simple noun of the API represents an important architectural design. From the point of view of architecture design (so-called composition theory), software systems are modules and interfaces. The module (Hierarchy/component) determines the division of labor, and the interface determines interaction. An API is the definition of an interface. The modules do not need to be concerned with the implementation of other modules, only need to know how to collaborate. This disperses the complexity into the various modules, making the overall system more controllable. The nature of the API is to provide the interface to the module developer, which is for "people (Programmer)". The core of the API's design task is to ensure that the user uses the interface at a lower cost and the driver module completes their business. For the public API, the biggest design challenge is how to do the API one at a time!

The author of the accompanying 1 mentions a "cluelessness" concept in which the user of the API does not need to have an understanding of the intrinsic logic of the API and can use the API only according to the API's definition. A bit more straightforward is the idiot-style API.

What is a good API

For general development tasks, it is often considered that the correctness of the function and the perfection of the design can be constantly tried to innovate and reconstruct. But these principles are not necessarily correct in the API design, but need to be somewhat conservative. Let's take a look at the good API standards KDE/QT developers have summed up:

Easy to learn and remember

(Easy to learn and memorize)
This includes naming, the use of patterns, and most crucially the inclusion of experiential programming. The so-called experiential programming means that developers often do not read through the interface of the document (if provided), but based on the continuity of thinking, with past experience to pre-assume the functionality of the API. For example, if the following two classes have the same method:

void Widget::SetSize(int width, int height);void View::SetSize(int width, int height);

Another class, logically considered to be a subclass of view, can be elusive by providing the following methods:

void Button::Layout(int width, int height);

From an empirical programming perspective, using Button::setsize () is a natural thing, and programmers will probably not be careful to verify that this button does not provide this method.
As an API designer, it is not assumed that the user will be careful to read all the documents, but to try to do two points:

    • Maintain a design consistent with universal cognition.
    • Maintain conceptual consistency in design (consistency).

Those who are recognized for their behavior and naming are very important, and do not innovate too much. Please observe the principle of minimum surprises.

Concise and clear semantics

This helps to understand and is difficult to misuse. When an API does not meet all of the requirements, do not try to affect the general scene for some very small scenes, you can separate the path of another. In this case, the parameters of the function are often reflected. For example, such an API (from Win32), you have to call the document every time:

HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);

In addition, in the attached 2, we give an example of the following HTML text output:

the <b>goto <u>label</b></u> statement

The implementation of C + + can be:

stream.writeCharacters("the ");stream.writeStartElement("b");stream.writeCharacters("goto ");stream.writeStartElement("i");stream.writeCharacters("label");stream.writeEndElement("i");stream.writeEndElement("b");stream.writeCharacters(" statement");

Obviously, the start and end of element here need to be handled by the developer themselves. If you want the compiler to help check and make the developer less fallible, the code can be changed to:

stream.write(Text("the ")        + Element("b", Text("goto ") + Element("u", "label"))        + Text(" statement"));
Easy to scale and backwards compatible

The previous information is scattered about both, and I'm merging them here because they are all necessary to consider the evolution of the API.
As demand changes, the evolution of the API is necessary, and there is no way to have an immutable API. But being a stable API is a promise to users, not just technically. The concept of stability is not constant, but the cost of change should be as low as possible.
If you add an API that will cause the previous code to fail to compile, or if the program does not execute properly, it will affect the user's trust in the API.

To encourage the writing of readability codes

As emphasized earlier, the API is for programmers, so the name itself must be readable. At the same time, it is designed to guide the user in writing more readable code. The following example is given in 2.
In Qt3, the constructor of the slider allows the user to specify multiple parameters:

slider = new QSlider(8, 128, 1, 6, Qt::Vertical, 0, "volume");

In Qt4, you need to do this:

slider = new QSlider(Qt::Vertical);slider->setRange(8, 128);slider->setValue(6);slider->setObjectName("volume");

Obviously the latter is more readable.

There is still a controversy here. It is not possible to separate the relevant things for the sake of readability alone, or to combine different content in order to simplify the code.

Simple

This is particularly important for the first article. An expanding, bloated API inevitably creates a variety of understanding and usage problems, especially when multiple APIs overlap. To give an example of a disturbing understanding:
void view::setsize (int width, int height);
void view::setwidth (int width);
void view::setheight (int height);
The latter two are clearly the two subtasks of the former, but are exposed for some particular reason. Will it come out whether to call SetSize () or the corresponding setwidth () or setheight () depending on the change?

Complete

If the functionality to be provided is to be provided, the functions (including setters/getters) that an interface class should have should also be provided in this class.

Design and implementation of API

The design implementation of the API, different backgrounds, different requirements will be described differently. I've summed up some of the key points they have in common.

Factory method is better than constructive function

If you expose a constructor, the object you create must be an instance of the class. The factory method is more flexible, although the parameters are identical, but you can return an instance of a subclass. At the same time, it is more advantageous to implement Singleton or cache object instance.
This type of application can often be seen on the interface of some chromium modules.

Constant modifier

Constant modifiers, which help limit unnecessary modification actions, are also a behavior contract. You can add a constant modifier to a parameter, function, or return value as needed.

Attribute-based APIs

Compared to the interface class that passes a string of arguments at construction time, it is better to set other parameters in the setter after construction. The difference is that the latter is more conducive to writing readable code. Here are some examples of the readability code above that are not mentioned here.
The point is that each attribute needs to be orthogonal and not order-independent.

Virtual APIs

There is always controversy over whether to provide an API in the form of virtual functions. This is not to discuss the definition of the interface class (Pure virtual Class), the necessity of defining the interface class is clear and no additional discussion is required.
In principle, the virtual function is restricted as an API because the override under inheritance may cause the interface behavior to become inconsistent because the behavior of the subclass cannot be determined.
But in some scenarios it is really necessary to provide a certain degree of extensibility for the user, you can provide virtual functions so that the user can change the original behavior through inheritance.

Boolean parameter

Using integer data instead of Enum is similar, the key is the user's understanding.
The practices that can be improved include a different function implementation, or an enumeration variable instead.
Example:

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

How to separate functions:

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

Using integers instead of lattice enumeration variables is the same problem.

Exception handling

In the attached 5, the author explains in detail the exception handling in the API. My summary is to throw only the exception must be thrown, should not be wise to silently deal with. The code of the API should be the most realistic response to the problem of implementation, not to use clever code to do some special processing. The reason behind this is that it makes the API's behavior deviate from expectations and violates the least surprising principle.

Named

In the name, attached 2 is listed in more detail. Summarized as follows:

    • Choose a name with self-explanatory ability
      The core is named from the perspective of the user and the domain, not from its own design. For example, QT 4.2 Qworkspace implements MDI (multiple document interface). Fortunately, such a name was later modified to Qmdiarea.
    • Names don't have to be ambiguous
      If you encounter APIs that are conceptually similar, be sure to differentiate them from the naming. such as Sendevent () represents a synchronous event, while Sendeventlater () represents an asynchronous event.
    • Maintain consistency
      This is important for support of pre-experienced programming, also known as symmetry (symmetry). If the set prefix represents setters, do not appear to begin with set, but not setter. Again, for example, the definition of setters/getters in chromium is independent in a very clear way.
    • Avoid shorthand
      In addition to being a generic abbreviation, do not arbitrarily define abbreviations in the form of acronyms. Otherwise, the reader may be completely unintelligible to the name.
    • Prefer to use special naming instead of generic naming
      A generic name often contains more general responsibilities, and should be reflected in the API if the functionality of the API comes with a clear application scenario. Otherwise, once you encounter the need for a common API, it is very redundant with the name xxxxingeneral and so on, and will make it difficult for users to choose the appropriate API.
    • Don't be too accommodating to the existing naming
      For example, when wrapping an old or sub-functional API, the original API name is often used. In fact, there is absolutely no need, more reasonable approach is to start from the new API function, choose the right name.
About backwards compatibility

The compatibility of a module (library) mainly includes:

    • API compatible
      The main definition is compatibility, that is, code can compile, and behavior consistency.

    • ABI compatible, which is binary level compatible.
      For shared libraries It is necessary to have the same symbol table, including global objects and definitions. There are too many such problems in Linux.

    • Compatibility of communication protocols
      If there is a custom protocol network communication, there may be a communication protocol between C/s compatibility issues.

    • Compatible with stored data and file formats
      If the user upgrade, found that the previous historical data is not available, most of the situation is unacceptable, and can not be a lawsuit.

Guaranteed compatibility

As to which points to ensure compatibility, depends on the size of the user, as well as the extent of impact (or the user's ability to withstand). From a compatibility standpoint, ensure that the compatibility method includes:

    • Don't throw away anything.
      A very sad reality. If you discard a portion of the API (and not change it), whether you use @deprecated, or if you repeatedly declare it in the document, you can invalidate the code before the user. Be sure to ensure the integrity of the previous API, unless your compatibility rules allow you to give up, like Microsoft claims that a version will no longer be supported.

    • Hide Details
      You can use opaque Pointer (PIMPL) or use constructors to help the API hide the internal data structure, and allow the user to manipulate the data only through the functions provided.

    • Guarantee the extensibility of protocols and data formats
      You can use standardized XML and a standardized protocol to replace a custom format. If the condition is not allowed, also remember to define the publication in the Agreement and data format, in order to do compatibility processing later.
      Reserved fields are also a common practice. I've been on more than one occasion, solving urgent problems by reserving fields in the agreement.

    • Guaranteed compatibility on implementation
      In the implementation of logic, especially in judgment processing should also pay attention to the compatibility of processing, this is a common mistake in the place. Take the processing of a field Flaga as an example:

      if (Headers.flaga! = 1) {
      DoB ();
      } else {
      DoA ();
      }

Obviously changing the judging condition to Headers.flaga = = 1 will make the implementation more compatible. Otherwise, when you downgrade, it's a disaster.

Extreme opinions are harmful

(main reference attached 1)
In the evaluation of API definitions, beauty or elegance is subjective. We should design an easy-to-use, widely accepted and productive API (Section 1). As for the principles defined, the completion depends on the requirements of the API itself. For performance reasons, for example, some APIs may not meet the requirements of certain scenarios and do not achieve completeness. The designer of the API does not need to satisfy everyone, it is important that the API itself remains forward-looking. For example, the standard optimization process is more appropriate for the development of APIs:
1. Make it
2. Make It Right
3. Make everything
4. Make Everything Right
5 .....

Reprint Please specify source: Http://blog.csdn.net/horkychen

On the design and implementation of API

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.