Home > Mobile >  How to create a wrapper or intermediate layer to access a class, without exposing it?
How to create a wrapper or intermediate layer to access a class, without exposing it?

Time:03-15

I use a third party engine, that has a class "Sprite". My classes use sprite, and call its methods.

There is a probability that "Sprite" will be replaced in the future by some other game engine. I would like to have a layer between my class, and Sprite, so that it is easy to swap out Sprite in future.

I figure there are at least two ways to do this:

  1. Implement a wrapper class that has a bridge method for every method in sprite, and that my code uses to access the sprite.

For Example:

Wrapper{
private:
Sprite* foo;
public:

  void method1(){
     foo->method1();
   }

   int method2(){
      return foo->method2();
    }
}

The downside with this approach is that there is a lot of work to write a method for each method in Sprite, even though all it is doing is just calling the method and returning whatever result. It is also a lot of maintenance work each time there is a change in sprite.

  1. Alternative 2 : Some kind of magic by overloading the -> operator.

     struct LoggingFoo : public Sprite {
     void log() const { } //Just a method for logging.Doesn't matter. 
    
    
     Foo const *operator -> () const { log(); return this; }
     Foo       *operator -> ()       { log(); return this; }
    

    };

Not very sure of all the things to keep in mind with this option ? For example, what happens to class methods ? Does it make sense to publicly inherit Sprite for this use case ?

Note: In practice, there is no object that is intended to inherit from Sprite in my code.

EDIT: What would be the most concise way to create the wrapper, yet expose all public member variables and functions? For example, not having to specify each and every variable and function to expose ?

CodePudding user response:

You just need to create a Wrapper class that publicly inherits from Sprite and use it. It automatically fully inherits all the methods and variables of the Sprite class in the Wrapper class with the same level of visibility:

class Sprite
{
    public:
        void foo(){};
        void bar(){};
        
        int mode = 0;
};

class Wrapper : public Sprite
{
};

int main()
{
    Wrapper w;
    w.foo();
    w.mode = 5;
    w.bar();
}

If in the future you switch to another library, you will inherit Wrapper from the new class and implement only removed or changed methods:

class NewSprite
{
    public:
        void foo(){}; // same interface
        void new_bar(int mode){};
};

class Wrapper : public NewSprite
{
    public:
        void bar() // wrap new method
        {
            new_bar(mode);
        }
        
        int mode = 0;
};

But a better approach would be to build a higher-level Wrapper interface so that when you completely change the library API, you don't have to rewrite every method:

class Wrapper
{
    public:
        void do_operation() // high-level interface
        {
            s_.foo();
            s_.mode = 5;
            s_.bar();
        }
        
    protected:
        Sprite s_;
};

class Wrapper
{
    public:
        void do_operation() // high-level interface
        {
            s_.foo();
            mode = 5;
            s_.new_bar(mode);
        }
        
        int mode = 0;
        
    protected:
        NewSprite s_;
};

int main()
{
    Wrapper w;
    w.do_operation();
}

CodePudding user response:

You could also consider a slightly different implementation to your wrapper using private (i.e., is implemented in terms of) inheritance.

This implementation removes the burden of wrapping every function and instead just add a using statement for every function you want to expose.

#include <iostream>

class Sprite
{
public:
    Sprite() : d_value(0) {}
    
    void method1() { std::cout << "Sprite::method1()\n"; }
    void method2() { std::cout << "Sprite::method2()\n"; }
    
    int d_value;
};

class Wrapper : private Sprite
{
public:
    using Sprite::method1;
    using Sprite::method2;
    using Sprite::d_value;
};

int main()
{
    Wrapper w;
    w.method1();
    w.method2();
    w.d_value = 3;

    return 0;
}

Live Example

  • Related