QC 6: Interfaces, Components & API

API stands for Application Programmer Interface or a similar definition as the case may be. In pure english terms, it could be described as the gateway through which another entity (program/programmer) interacts with the entity that the API embodies/personifies. Though the above definition is a round-about description, I did intend to avoid *interface* in general due to the reasons described below.

Interface is a commonly used terminology in Software Development and typically signifies the window through which 2 entities talk. It is also commonly referred to as the common language used by 2 software modules to interact. However, interface has a very specific definition from a software design perspective. Design patterns deal extensively with the definition of terms like interface, component, framework, toolkit etc. I would definitely recommend some amount of study on these terminologies to gain a better understanding.

Borrowing the definitions from these gyan gurus, I would like to term interface as a set/collection of functions/methods in it’s most elementary form. These methods are *defined* by an interface and *implemented* by a component.  According to me, this is a very critical distinction that needs to be well understood and imbibed. Definition of an entity only indicates to the defining the template of the entity. Hence, when we say interface defines the methods, we mean that interface only provides the function definitions/templates (not to be confused with C++ templates).

The question is who will provide the definitions of these functions. This is where the concept of a component-interface-model comes in. As noted earlier, interfaces defines a set of functions. If a component say C implements an interface, then the component provides the definition for that function. Hence, it is imperative that any component can choose to implement any interface or any number of interfaces in its own unique way.

Now, in a large software system, there will be multitude of components, each of them implementing the same/similar interface.  Hence, when more than one component provides its own unique implementation of the interface, we can note that an interface has multiple implementations which is specific to the component implementing the same.

In a nutshell, one interface can be implemented by many components. A component can implement any number of interfaces.

This concept is explained through a series of code snippets below:

An example illustration of an interface called IAlgorithm interface is as below. This interface defines the standard set of functions to be supported by any component that implements an algorithm.

Class IAlgorithm
{
     virtual void Create(void **) {} = 0;
     virtual void Init(void *, void *) {} = 0;
     virtual void Run(void *, void *) {} = 0;
     virtual void Delete(void *) {} = 0;
}

Another interface called IDebugStats is defined below. This interfaces defines a set of methods that are employed to dump statistics from any component for offline analysis.

Class IDebugStats
{
      virtual void DumpStats(char *) {} = 0;
}

Consider 3 components Component1, Component2, Component 3 which are defined as below. Component 1 only implements IAlgorithm where as both Components 2 and 3 implement IAlgorithm and IDebugStats as shown.

Component1: public IAlgorithm

Class Component1 :: public IAlgorithm
{
      void Create(void **handle)
      {
            cComponent *newComp = new cComponent;
            *handle = newComp;
            return;
      }

      void Init(void *handle, void *init_stats)
      {
            cComponent *pHdl = (cComponent *)(handle);
            return pHdl->init_method(init_stats);
      }

      void Run(void *handle, void *run_params)
      {
            cComponent *pHdl = (cComponent *)(handle);
            return pHdl->run_method(run_params);
      }

      void Delete(void *handle)
      {
            cComponent *pHdl = (cComponent *)(handle);
            if(pHdl->delete_method())
            {
                  *handle = NULL;
                  return;
            }
      }
}

Component2: public IAlgorithm, public IDebugStats

Class Component2 :: public IAlgorithm, public IDebugStats
{
      void Create(void **handle)
      {
            instance_t *newComp = instance_t.createComponent();
 *handle = newComp;
            return;
      }

      void Init(void *handle, void *init_stats)
      {
            instance_t *pHdl = (instance_t *)(handle);
            return instance_t->initialize(init_stats);
      }

      void Run(void *handle, void *run_params)
      {
            instance_t *pHdl = (instance_t *)(handle);
            return instance_t->execute(run_params);
      }

      void Delete(void *handle)
      {
            instance_t *pHdl = (instance_t *)(handle);
            if(pHdl->delete_instance())
            {
                  *handle = NULL;
                  return;
            }
      }
      
 char MODULE_STRING[] = "Component2";
      void DumpStats(char *str)
      {
          printf("[%s]: %s", MODULE_STRING, str);
      }
}

Component3: public IAlgorithm, public IDebugStats

Class Component3 :: public IAlgorithm, public IDebugStats
{
      void Create(void **handle)
      {
            instance_t *newComp = instance_t.createComponent();
 *handle = newComp;
            return;
      }

      void Init(void *handle, void *init_stats)
      {
            instance_t *pHdl = (instance_t *)(handle);
            return instance_t->initialize(init_stats);
      }

      void Run(void *handle, void *run_params)
      {
            instance_t *pHdl = (instance_t *)(handle);
            return instance_t->execute(run_params);
      }

      void Delete(void *handle)
      {
            instance_t *pHdl = (instance_t *)(handle);
            if(pHdl->delete_instance())
            {
                  *handle = NULL;
                  return;
            }
      }
      
 char MODULE_STRING[] = "Component2";
      void DumpStats(char *str)
      {
          FILE *f_hdl = fopen("/tmp/debugStats.log", "a");
          fprintf(f_hdl, "[%s]: %s", MODULE_STRING, str);
          fclose(f_hdl);
      }
}

From these code snippets, it is very evident that each component chooses to implement each of the interfaces (same definition) in its unique way. Similarly, each interface (whether IAlgorithm or IDebugStats) has its own unique implementation provided by the individual components.

Application Programmer Interface

API are the gateway through which any software module/program interacts with any other software module/program. API doesn’t preclude that only Software entities can be modelled through API. If there is a HW block which is programmed through Registers and the state machine is controlled by updating the same, then these set of registers combined with the values that they can take are considered to be the API for the HW block. In general, is an interface for anyone to talk to anyone else.

In Software programming, design of a very good and stable API is of utmost importance. APIs should be designed with thorough investigation and enough thought process being put in.  APIs should *not* keep evolving throughout the development cycle i.e. APIs once frozen are adhered to throughout the lifecycle of the product. A typical checklist of Do’s and Don’ts with API is enlisted below:

  • Do Design APIs at the starting of the project; Once frozen, Don’t change the existing APIs at any cost
  • API’s should be designed in a modular fashion i.e. abstract functionality and intent
  • Don’t overdo the design i.e. if a module has 37 parameters exposed, don’t expose 37 functions for anyone to set and another 37 to read i.e. Minimal number of functions providing complete access to the module.
  • Do keep your API’s always backward compatible; Don’t break backward compatibility at any cost
  • Do communicate the new functions introduced very clearly through documentation
  • Preferably, do maintain a version number for API. This helps in maintaining backward compatibility and provides a flexibility to introduce new functions.
  • Last but definitely not the least, Keep It Simple, Silly/Stupid

In general, a little bit of diligence, applied thought process and good design helps to design a good API, which then can facilitate easier integration with no/minimal pain points. Simple and straight APIs go a long way to generate high-quality software. It won’t be understatement to say that the quality of the software can be gauged by looking at the API of the module.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s