4

I'm currently working on a project that involves creating an abstraction layer. The goal of the project is to support multiple implementations of server software in the event that I might need to switch over to it. The list of features to be abstracted is rather long, so I'm going to want to look into a rather painless way to do it.

Other applications will be able to interact with my project and make calls that will eventually boil down to being passed to the server I'm using.

Herein lies the problem. I haven't much experience in this area and I'm really not sure how to make this not become a sandwich of death. Here's a chain of roughly what it's supposed to look like (and what I'm trying to accomplish).

/*
Software that is dependent on mine
    |
Public API layer (called by other software)
    |
Abstraction between API and my own internal code (this is the issue)
    |
Internal code (this gets replaced per-implementation, as in, each implementation needs its own layer of this, so it's a different package of entirely different classes for each implementation)
    |
The software I'm actually using to write this (which is called by the internal code)
*/

The abstraction layer (the one in the very middle, obviously) is what I'm struggling to put together.

Now, I'm only stuck on one silly aspect. How can I possibly make the abstraction layer something that isn't a series of

public void someMethod() {
    if(Implementation.getCurrentImplementation() == Implementation.TYPE1) {
        // whatever we need to do for this specific implementation
    else {
        throw new NotImplementedException();
    }
}

(forgive the pseudo-code; also, imagine the same situation but for a switch/case since that's probably better than a chain of if's for each method) for each and every method in each and every abstraction-level class.

This seems very elementary but I can't come up with a logical solution to address this. If I haven't explained my point clearly, please explain with what I need to elaborate on. Maybe I'm thinking about this whole thing wrong?

  • Why is the middle level necessary? The way I see it, the 2nd layer (public API), is an interface (or set of interfaces) and the 4th layer (Internal code) represents interface implementations, or concrete classes, that vary by implementation. All that's needed is a one-time configuration to select the appropriate implementation class and the rest would work out of the box. – shmosel Jul 22 '16 at 22:10
  • @shmosel It might not be. That represented a "layer" because I'm not sure if it's the right solution to my problem. I wasn't too sure of what I'm supposed to be doing in that "level", if even have the level at all. –  Jul 23 '16 at 00:31

1 Answers1

1

Why not using inversion of control ?

You have your set of abstractions, you create several implementations, and then you configure your public api to use one of the implementations.

Your API is protected by the set of interfaces that the implementations inherit. You can add new implementations later without modifying the API code, and you can switch even at runtime.


I don't know anymore if inversion of control IS dependency injection, or if DI is a form of Ioc but... it's just that you remove the responsibility of dependency management from your component.

Here, you are going to have

  • API layer (interface that the client uses)
  • implementations (infinite)
  • wrapper (that does the IoC by bringing the impl)

API layer:

// my-api.jar
public interface MyAPI {
    String doSomething();
}

public interface MyAPIFactory {
    MyAPI getImplementationOfMyAPI();
}

implementations:

// red-my-api.jar
public class RedMyAPI implements MyAPI {
    public String doSomething() {
        return "red";
    }
}

// green-my-api.jar
public class GreenMyAPI implements MyAPI {
    public String doSomething() {
        return "green";
    }
}

// black-my-api.jar
public class BlackMyAPI implements MyAPI {
    public String doSomething() {
        return "black";
    }
}

Some wrapper provide a way to configure the right implementation. Here, you can hide your switch case in the factory, or load the impl from a config.

// wrapper-my-api.jar
public class NotFunnyMyAPIFactory implements MyAPIFactory {
    private Config config;

    public MyAPI getImplementationOfMyAPI() {
        if (config.implType == GREEN) {
            return new GreenMyAPI();
        } else if (config.implType == BLACK) {
            return new BlackMyAPI();                
        } else if (config.implType == RED) {
            return new RedMyAPI();                
        } else { 
           // throw...
        }
    }
}

public class ReflectionMyAPIFactory implements MyAPIFactory {
    private Properties prop;

    public MyAPI getImplementationOfMyAPI() {
        return (MyAPI) Class.forName(prop.get('myApi.implementation.className'))
    }
}

// other possible strategies

The factory allows to use several strategies to load the class. Depending on the solution, you only have to add a new dependency and change a configuration (and reload the app... or not) to change the implementation.

You might want to test the performances as well.

If you use Spring, you can only use the interface in your code, and you inject the right implementation from a configuration class (Spring is a DI container). But no need to use Spring, you can do that on the Main entry point directly (you inject from the nearest of your entry point).

  • The my-api.jar does not have dependencies (or maybe some towards the internal layers).
  • All the jar for implementations depend on my-api.jar and on you internal code.
  • The wrapper jar depends on my-api.jar and on some of the impl jar.

So the client load the jar he wants, use the factory he wants or a configuration that inject the impl, and use your code. It depends also on how you expose your api.

chikincrow
  • 393
  • 3
  • 11
  • [Inversion of control](https://stackoverflow.com/questions/3058/what-is-inversion-of-control)? I'm confused about *how* to actually do this in practice, though. –  Jul 23 '16 at 00:45
  • Much more thorough now, thanks. :) Thanks to some collaboration between you and a friendly face on IRC, I was able to put it together. –  Jul 23 '16 at 20:12