Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly decouple classes from frameworks (source level decoupling)

I am working on a game project using Microsoft XNA framework, although this question is a generic one that involves decoupling of classes/systems.

Let's say i have a class (simplified to suit this example) as shown here:

public class GameCell
{   
        public Color Color { get; set; }
}

I would like to reuse as much code as i can between multiple platforms that use C#.

The problem of directly referncing the Color struct is that this type is defined in the XNA assemblies, creating a strong coupling (or dependency) between my code and XNA.

The other framework i will be using may (or may not) have its own Color object, with its own set of properties and API.

I would like to have the same source file "know" to use either the XNA or other framework's implementation automagically.

I know other types of decoupling methods such as IoC, but that would mean i would be plugging in different versions of a system/class, instead of reusing the same class in different contexts.

Is this even possible to do? how would u suggest to keep such a system portable?

I've seen some cases (in native C++ development) where you would define a set of classes that mirror the classes the framework you're using has (for example here - define Color again), and so it is possible to "re-map" this later on to use different classes, upon need.

Another option is to mess around using #IFDEF to play with the using statements in the header of the class (switching from XNA to other platforms). What is the best alternative in this case?

like image 210
lysergic-acid Avatar asked Dec 02 '11 20:12

lysergic-acid


1 Answers

Typically what you do is create your own Color structure and have it do the translation using conditional compilation switches. For example:

#define USE_DOTNET
//#define USE_SOMETHINGELSE

#if USE_DOTNET
using System.Drawing;
#endif

#if USE_SOMETHINGELSE
using SomethingElse.Drawing;
#endif

public struct MyColor
{
    #if USE_DOTNET
    Color TheColor;
    #endif
    #if USE_SOMETHINGELSE
    SomethingsColor TheColor;
    #endif

    public int R
    {
        get
        {
            #if USE_DOTNET
                // code to return .NET Color.R value
            #endif
            #if USE_SOMETHINGELSE
                // code to return SomethingColor.R value
            #endif
         }
    }
}

Another way is to have separate regions for the .NET and SomethingElse code. For example:

#if USE_DOTNET
public int R
{
    get { return TheColor.R; }
}
// other properties and methods here
#endif

#if USE_SOMETHINGELSE
// properties and methods for using SomethingElse
#endif

This can work well, but you have to be very careful when writing your code so that you don't use the .NET Color structure anywhere but in these portability layers.

We used this technique in C/C++ when developing games that had to run on Windows, the Mac, and several different game consoles. It's a pain to set up the portability layers, but once they're set up, it's pretty easy to use.

I would caution you, though, not to give your portability classes and structures the same names as classes or structures from the .NET libraries or the other libraries you're using. If you do that, you're going to confuse yourself, and you're likely to write some non-portable code that you won't discover until you start trying to port it. (Not that I'm speaking from experience or anything . . .)

like image 173
Jim Mischel Avatar answered Oct 12 '22 08:10

Jim Mischel