Dealing with Legacy Code Part 2: Extract the API out of your interfaces


Think about the following situation:

There is an interface / class interface / function in your application used by others which conains non-standard enums / types / whatever coming from an external API like OpenGL, DirectX, some kind of commercial API. The underlying code strongly uses this API. So using parts of this API was straight forward. Instead of having a strong encapsulation your API was spreaded all oer your codebase. Unfortunately the Product-Manager generates this requests: we need to exchange the underlying API because of whatever … Easy, just change this one API below your code and … wait a minute. There are all these symbols coming from the old API. WTF!

It looks like:

enum class API1Enum {
  feature1,
  feature2
};

class MyInterface {
public:
  void enableFeature( API1Enum myEnum ) = 0;
};

// Implementation for API1
class MyAPI1Impl : public MyInterface {
  void enableFeature( API1Enum myEnum ) {
    // handle the enum from the API1
    switch (myEnum) { ... }
  }
};

For the first implementation this concept works fine. Now you get a new API to wrap, just implement the interface, right?

class MyAPI2Impl : public MyInterface {
  // Damned, API1 does not exist anymore ...
  void enableFeature( API1Enum myEnum ) {
    ..
  }
};

Solution: Wrap the enum as well:

Of course you can introduce another app-specific enum, wrap the API by using it and you are fine:

// From the API
enum class API1Enum {
  feature1,
  feature2
};

// defined in your app
enum class AppEnum {
  Appfeature1,
  Appfeature2,
  AppNotSupported
};

// change your interface
class MyInterface {
public:
  void enableFeature( AppEnum myEnum ) = 0;
};

// Introduce functions for translation
static API1Enum translateAPI1( AppEnum type ) {
  switch( type ) {
    case AppEnum::Appfeatue1; return API1Enum::feature1;
    case AppEnum::Appfeature2: return API1Enum::feature2;
  }
  // error handling
}

static API2Enum translateAPI2( AppEnum api2_type ) {
  // do it for API2
}

class MyAPI1Impl : public MyInterface {
  void enableFeature( AppEnum myEnum ) {
    // translate AppEnum and handle it for API1
    switch (translateAPI1(myEnum)) { ... }
  }
};

class MyAPI2Impl : public MyInterface {
  void enableFeature( AppEnum myEnum ) {
    // translate AppEnum and handle it for API1
    switch (translateAPI2(myEnum)) { ... }
  }
};

What you are doing:

  1. Introduce an APP-specific enum
  2. Use this enum instead of the API-specific enum
  3. Introduce translation functions to translate the AppEnum to the API1Enum / API2Enum
  4. For the API-specific implementations: translate the AppEnum into the API-specific enum

Optimizations?

You can use lookup-tables instead of the translation-functions. But as a first step translation-functions a much easier to debug.

Leave a Reply

Your email address will not be published.