首页 > 解决方案 > 这是策略模式吗

问题描述

为了从一个 http 客户端移动到另一个(相同的概念可以应用于日志库、数据库),我实现了这个解决方案,但我不确定它是否是 OOP 世界中的一种模式。在看了一些之后,它看起来有点像策略模式或存储库模式。但是我从存储库模式中了解到,它仅限于数据访问功能。然而,同样的概念可以应用于日志库、http 客户端或电子邮件客户端等。

这是我不知道名称的实现:( =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));

标签: javascriptnode.jstypescriptoopdesign-patterns

解决方案


在回答之前我想说的是,Strategy模式及其派生的思想就是利用dynamic dispatchand这种方式来代替手动管理条件逻辑在代码中进行行为参数化,留下多态/条件行为的选择到运行时。IMO 策略模式是多态的最基本和最实用的形式。动态类型语言使用鸭子类型来做到这一点,但想法是一样的。

您在这里使用了某种策略模式,利用了我需要添加的鸭子类型,因为typescript问题中有一个标签(实际上,使用 ES6 添加的 typescript 和 class 关键字不会改变 javascript 的性质是一种使用 protoypal 继承的语言,但是如果我们用这种语法糖来考虑 GoF,我需要添加这个)。

我在这里看到了一些问题。第一个是是否值得付出努力,因为您可以直接使用axios.get(url).then(...),或者fetch(url).then(...)我的意思是在这种情况下没有真正的多态行为,这里的变化或条件行为只是 http 客户端和次要方法(httpClient.get/x/y/z().then() to httpClient.url().then())以及来自如果您的代码仅保留单个方法,则此观点也看起来像 a command pattern,这意味着仅使用 lambda 函数。但是,如果您的目的是创建一个库,并且您的具体类将成为 http 客户端多个方法之上的包装器,然后为用户提供选择,那么它会更好。所以你的混凝土将是包装材料。

一些 GoF 方法实际上是包装器,它们的区别在于它们的intents ,实际上 aFacade在这里就足够了。

使用经典继承和静态类型,使用策略/策略模式的想法可能如下:

客户端/驱动程序类是:

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

选择具体实现的基本工厂类:

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

这实际上是不必要的,只是httpClients['axios'] with httpClients object/map在上面使用。

参数化行为:

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();
  }
}

这也与您的代码大致相似。

虽然这么说你可以只使用 lambdas 来提供这种抽象:

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));
 

推荐阅读