Home > Back-end >  Wrap an interface around a class from library
Wrap an interface around a class from library

Time:06-05

There is a class defined in a library:

class fromLib{};

used as argument to invoke a method implemented within the library :

 method_from_lib(const fromLib& from_lib){};

I want wrap this method within an interface

  class Methods{
       virtual invoke_method(const& genericArg)
  } 

where class genericArg is generic representation of fromLib.

A simple implementation would be:

 class MethodImpl: public Methods { 
     invoke_method(const genericArg& arg) { 
           method_from_lib(arg); // this is not going to work since the argument is not an instance of `fromLib` nor convertible nor does it inherit from it. 
     }; 
 }

In an ideal world, I would have made the class fromLib directly inherit from genericArg . However, this is not possible given that this class is from a library that I really dont want to touch.

How would I achieve a wrapper around fromLib which is also an implemetation of genericArg.

The motivation for this is the following:

A library provides some method with the signature method_from_lib(const fromLib& from_lib){}; that we might or might not use. We potentially want to either use that method or some other implementation. If we were to use some other implementation the argument to the function would also need to change since the argument fromLib is tightly coupled to the implementation provided by the library.

Therefore, providing an interface for the method itself is straightforward, however, the purpose of this question is to how we can generalize the argument fromLib into some interface. So that our interface would be like

class Methods{ virtual invoke_method(const& genericArg) }

When we want to implement the case with using the library, the implementation is fairly easy we just invoke the method from the library i.e.

 invoke_method(const TYPE& arg) { 
       method_from_lib(arg); 
 }; 

Now as you see implementing this method either requires the TYPE to be genericArg or some class that inherits from it. The problem here is that method_from_lib expects the type fromLib which we cannot directly make as a child from genericArg since that type is contained in a thirdparty library

CodePudding user response:

I would suggest using composition and passing the member into the library call:

struct GenericArg{
    fromLib member;
};

void wrapper(const GenericArg& arg){
   method_from_lib(arg.member);
}

CodePudding user response:

I think you're looking for an adapter.

https://refactoring.guru/design-patterns/adapter/cpp/example

Usage examples: The Adapter pattern is pretty common in C code. It’s very often used in systems based on some legacy code. In such cases, Adapters make legacy code work with modern classes.

Identification: Adapter is recognizable by a constructor which takes an instance of a different abstract/interface type. When the adapter receives a call to any of its methods, it translates parameters to the appropriate format and then directs the call to one or several methods of the wrapped object.

You state you don't want to touch the fromLib class. Under the assumption that the data required by fromLib will definitely be required for the operation that is to be executed, you'll have to implement your own struct which contains the required data.

This own struct can then be passed to the adapter. The adapter will then pass it on to the lib, and return the correct result. If you want to switch out the lib, you switch out the adapter to work with the new lib. The rest of your code will remain unchanged.

E.g.:

class fromLib{
    int degrees;// You can't change this even if you'd want to use radians in your program.
}

struct myActualRequiredData {
    double radians;// Choose something here which works well within your own program.
}

class MethodsTarget{
    virtual invoke_method(const myActualRequiredData& data);
} 

class MethodsAdapter : public MethodsTarget{
    virtual invoke_method(const myActualRequiredData& data) {
        fromLib myFromLib = {
            .degrees = radians * 360 / PI;// Or so. I currently don't care, not what your question is about. In any case, here's where you do all the work to speak the libraries language.
        };

        method_from_lib(myFromLib);
    }
};

MethodsAdapter adapter;
adapter.invoke_method({
    .radians = 0.2
});

// Oh look I found a better lib
class fromLib2{
    std::string degrees;
}

class MethodsAdapter2 : public MethodsTarget {
    virtual invoke_method(const myActualRequiredData& data) {
        fromLib2 myFromLib = {
            .degrees = std::string(radians * 360 / PI);
        };

        method_from_lib(myFromLib);
    }
}

MethodsAdapter2 adapter2;
adapter.invoke_method({
    .radians = 0.2
});

This example shows that changing the library will lead to minimal adjustment when using the adapter pattern.

Use with pointers to MethodsTarget if necessary. But usually for libs, simply having one adapter class should be enough. You won't be hotswapping anyways.

  • Related