Home > Net >  Design pattern to make dependencies interchangeable
Design pattern to make dependencies interchangeable

Time:07-25

In the application I work on, we have a function called makeRequest. This function takes some parameters and then executes network calls to other systems. The beauty of this design is, if we wanted, we could switch our request library and the code would only need to be changed in one place. We currently use axios, but we could switch that out for superagent or native fetch and none of the external code would need to be changed. The interface decouples the inner workings of the function by creating an agnostic interface used by the rest of the application.

I know this is a design pattern. My question is what is its name? Is this the adapter pattern? The facade pattern?

CodePudding user response:

The pattern you're looking for is Facade, it allows you to create your own api as an extra layer over axios. This way you can replace axios with superagent and your client code would never notice that. Here's some of my links:

This is my repo about design patterns examples

This is an old repo of mine, its a bit outdated but is still relevant for your case

The @SetUp's answer would be correct if you need to have multiple http clients at the same time and use one or another based on the user needs/actions. But you need just one, if you change your http lib for superagent I'm pretty sure you will uninstall axios.

CodePudding user response:

The name of pattern is Strategy. In addition, it is necessary to use Factory method pattern to resolve request instances of strategies.

You can choose any strategy at the runtime based on your parameter.

Let me show an example of implementation via TypeScript. So we need some request types:

type RequestStrategyType = 'axios' | 'superagent' | 'fetch';

And some abstractions that can be implemented by request strategies:

interface RequestStrategy {
    name: RequestStrategyType;
    fetch: () => any;
}

abstract class BaseRequestStrategy implements RequestStrategy {
    constructor(public name: RequestStrategyType) { }

    fetch() {
        return `fetched by BaseRequestStrategy`;
    }
}

And its concrete implementations:

class AxiosRequestStrategy extends BaseRequestStrategy {
    constructor() {
        super('axios');
    }

    fetch() {
        return `fetched by AxiosRequestStrategy`;
    }
}

class SuperagentRequestStrategy extends BaseRequestStrategy {
    constructor() {
        super('superagent');
    }

    fetch() {
        return `fetched by SuperagentRequestStrategy`;
    }
}

class FetchRequestStrategy extends BaseRequestStrategy {
    constructor() {
        super('fetch');
    }

    fetch() {
        return `fetched by FetchRequestStrategy`;
    }
}

And it is necessary to implement Factory pattern to generate request strategies by RequestStrategyType:

class RequestStrategyFactory {
    create(type: RequestStrategyType) {
        switch (type) {
            case 'axios':
                return new AxiosRequestStrategy();
            case 'superagent':
                return new SuperagentRequestStrategy();
            case 'fetch':
                return new FetchRequestStrategy();
            default:
                console.log(`The ${type} strategy is not available. 
                    Falling back to default request strategy.`)
                return new AxiosRequestStrategy();
        }
    }
}

And this is a class which works with RequestStrategyFactory:

class RequestClient {
    strategyFactory = new RequestStrategyFactory();

    public make(strategyType: RequestStrategyType) {
        return this.strategyFactory.create(strategyType);
    }
}

And then code can be called like this:

const requestClient = new RequestClient();
const axiosClient = requestClient.make('axios');
console.log(`axiosClient`, axiosClient)

const superagentClient = requestClient.make('superagent');
console.log(`superagentClient`, superagentClient)

const fetchClient = requestClient.make('fetch');
console.log(`fetchClient`, fetchClient) 
  • Related