简体   繁体   中英

Is this strategy pattern

To move from one http client to another (same concept can be applied to log libs, db's) I implemented this solution but I am not sure if it's a pattern in OOP world. After looking at some it kinda looks like strategy pattern or repository pattern. But what I understood from repository pattern it is only restricted to data access functionality. However same concept can be applied logging libs, http clients or email client etc.

Here is my implementation of something that I don't know the name: ( =D )

const fetch = require('node-fetch');
const axios = require('axios');
class MyHttpAxios {
  constructor(axios) {
    this.axios = axios;
  }

  async get(url) {
    const response = await this.axios.get(url);
    return response.data;
  }
}

class MyHttpFetch {
  constructor(fetch) {
    this.fetch = fetch;
  }
  async get(url) {
    const response = await this.fetch(url);
    return response.json();
  }
}

const httpClients = {
  axios: new MyHttpAxios(axios),
  fetch: new MyHttpFetch(fetch),
};

const client = httpClients['axios'];
client
  .get('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response)
  .then(json => console.log(json));

Before the answer I would like to say that, the idea in Strategy pattern and its derivations is to take the advantage of dynamic dispatch and in this way instead of manually managing conditional logic to make behavior parameterization in the code, leaving the selection of polymorphic/conditional behavior to the runtime. IMO strategy pattern is the most basic and practical form of polymorphism. Dynamic-typed languages does it with duck typing but the idea is same.

You are using some kind of strategy pattern here by taking the advantage of duck typing which I needed to add this also, because there is a typescript tag in the question (Actually typescript and class keyword added with ES6 doesn't change the nature of javascript which is a language using protoypal inheritance, but If we think in terms of GoF with this syntactical sugar I needed to add this).

I see a few issues here. First one is whether is it worth the effort or not because you could directly use axios.get(url).then(...) or fetch(url).then(...) which I mean there is no really a polymorphic behavior in this case, what change or the conditional behaviour here is just the http client and minor method( httpClient.get/x/y/z().then() to httpClient.url().then() ) and from this perspective if your code would only keep single method also look like a command pattern which implies the usage of just lambda functions. However if your purpose is to create a library and if your concrete classes will be a wrapper above http clients' multiple methods and then provide users a selection then it would be finer. So your concretes will be wrappers.

Some of the GoF methods are actually wrappers and what distinguish them is their intent s and actually a Facade would be enough here.

With classical inheritance and static typing, the idea to use Strategy/Policy pattern is could be below:

The client/driver class is:

class Client {
    fetchPolicy = PolicyFactory.getFetchPolicy('axios');
    data = new Fetcher().fetchSomeData(fetchPolicy, 'https://jsonplaceholder.typicode.com/todos/1');
}

A basic factory class to choose concrete implementation:

class PolicyFactory {
    const httpClients = {
      axios: new MyHttpAxios(),
      fetch: new MyHttpFetch(),
    };
    public static getFetchPolicy(policyType: string): FetchPolicy {
       return Object.keys(httpClients).find(key => key === policyType);
    }
}

Which is actually unnecessary and just using httpClients['axios'] with httpClients object/map does above.

Parameterized behaviour:

interface FetchPolicy {
   get(url: string): any;
}

class MyHttpAxios implements FetchPolicy {
  async get(url) {
    const response = await this.axiosGet(url);
    return response.data;
  }
  
  private axiosGet(url) {
     axios // Since it is global use directly, or if you don't pass as a parameter in the client
      .get(url)
      .then(response => response)
      .then(json => console.log(json));
  }
}

class MyHttpFetch implements FetchPolicy {
  async get(url) {
    const response = await this.fetch(url).then(/*...*/);
    return response.json();
  }
}

Which also roughly similar to your code.

While said this you could just use lambdas to provide this kind of abstraction:

const fetch = require('node-fetch');
const axios = require('axios');

((url, httpClientFn, transformFn) => {
                                      return httpClientFn(url).then(response => transformFn(response))
                                    })("http://...", url => axios.get(url), response => console.log(response));

//somewhere in other clients which use the fetch
((url, httpClientFn, transformFn) => {
                                      return httpClientFn(url).then(response => transformFn(response))
                                    })("http://...", url => fetch.url(url), response => console.log(response));
 

I think that is more an array of Classes than a true Strategy Pattern. Should that, what you have created, be considered a pattern? I doubt it, since OOP design patters are mainly based on inheritance to reduce code/design complexity.

Your solution needs one more step to be considered a pattern. Becasue, at this point your classes MyHttpAxios and MyHttpFetch have no relationship between them.

What is a Strategy Pattern structure for your case? Based on the "oficial" structure of the design pattern, your design should look something like:

 ---------------           -----------
|  SomeClass    | <>----- | MyHttp<I> |
| client:MyHttp |         |           |
 ---------------          | +get()    | 
                           -----------
                            ^    ^
                            /    \
                           /      \
              ---------------   ---------------
             | FetchStrategy | | AxiosStrategy |
             | +get()        | | +get()        |
              ---------------   ---------------

So, SomeClass is where you will instantiate your Strategy. MyHttp is an Interface (not planed to have instances). FetchStrategy and AxiosStrategy are your clases where you implement your code (very similar to your code).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM