Monday, July 4, 2022

C++ API Design Best Practices - EASY TO USE


Easy-to-use API

Easy-to-use API means the client should be able to look at API's method signatures and understand the methods and be able to use them without any additional documentation. anyway, documentation can be provided, but providing some example codes showing how to use features in your API is much more efficient and will be easier for clients to understand features in API.

Discoverable

  • A  discoverable API is one where users are able to work out how to use the API on their own, without any accompanying explanation or documentation.

Misuse

  • A good API, in addition to being easy to use, should also be difficult to misuse.
  • Some of the most common ways to misuse an API include passing the wrong arguments to a method or passing illegal values to a method.
  • These can happen when you have multiple arguments of the same type and the user forgets the correct order of the arguments or where you use an int to represent a small range of values instead of a more constrained enum type
  • Prefer enums to booleans to improve code readability.

            enum SearchDirection 
            {
                    FORWARD,
                    BACKWARD
            };
  • Following is the incorrect way to pass the argument as a boolean because arguments can mixup 
                result = SearchDirection (text, true, false);
  • Following is the correct way to pass the argument as boolean because arguments cannot mixup 
                result = FindString(text, FORWARD, CASE_INSENSITIVE);

  • Avoid functions with multiple parameters of the same type. the following example, the client may pass the wrong integers 
               class Date
               {
                         public:
                                   Date(int year, int month, int day);
 
               };
  • Use arguments as objects and each class specifies Year, Month, and Date
               class Date
               {
                        public:
                                 Date(const Year &y, const Month &m, const Day &d);
                };
  • The use of consistent method signatures is equally critical to design quality. If you have several methods that accept similar argument lists, you should endeavor to keep a consistent number and order for those arguments.
    • Naming conventions (use camelCase and m_variable)
    • Parameter order (order from smallest type to large type  at last boolean-byte-int-double)
    • The use of standard patterns (Use verbs for methods getPersonId(); do not use getPId(); )
    • Memory model semantics
    • The use of exceptions (so that API won't crash)
    • Error handling

Orthogonal 

  • An orthogonal API means that functions do not have side effects. Calling a method that sets a particular property should change only that property and not additionally change other publicly accessible properties. As a result, making a change to the implementation of one part of the API should have no effect on other parts of the API
    • Reduce redundancy. Ensure that the same information is not represented in more than one way. There should be a single authoritative source for each piece of knowledge.
    • Increase independence. Ensure that there is no overlapping of meaning in the concepts that are exposed. Any overlapping concepts should be decomposed into their basal components.

Memory Allocation

  • C++ memory management is hard compared to Java or C#, where objects are freed automatically by a garbage collector.
    • Null dereferencing: trying to use -> or * operators on a NULL pointer.
    • Double freeing: calling delete or free() on a block of memory twice.
    • Accessing invalid memory: trying to use -> or * operators on a pointer that has not been allocated yet or that has been freed already.
    • Mixing Allocators: using delete to free memory that was allocated with malloc() or using ree() to return memory allocated with new.
    • Incorrect array deallocation: using the delete operator instead of delete [] to free an array.
    • Memory leaks: not freeing a block of memory when you are finished with it.
    • Always use Smart Pointers instead of raw pointers, because smart pointers will handle memory for you since they are objects.
    • Return a dynamically allocated object using a smart pointer if the client is responsible for deallocating it.
  • Ownership of MyObject* is transferred to the caller
                    boost::shared_ptr<MyObject> GetObject() const;
  • Ownership of MyObject* is retained by the API
                    MyObject* GetObject() const;
  • Think of resource allocation and deallocation as object construction and destruction.

Platform Independent 

  • Never write public header files with platform-specific #ifdef lines. It exposes implementation details and makes your API appear different on different platforms

No comments:

Post a Comment

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...