【问题标题】:Is this strategy pattern这是策略模式吗
【发布时间】:2020-07-24 09:32:33
【问题描述】:

为了从一个 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));

【问题讨论】:

  • 你有什么问题?如果是标题中的内容,您期待的是还是否?
  • 存储库模式用于抽象数据库访问并为规范类型查询提供接口。我认为您的代码与此无关。

标签: javascript node.js typescript oop design-patterns


【解决方案1】:

在回答之前我想说的是,策略模式及其派生的想法是利用dynamic dispatch,通过这种方式而不是手动管理条件逻辑来在代码中进行行为参数化,留下选择运行时的多态/条件行为。 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()),从这个角度来看,如果您的代码只保留单个方法也看起来像command pattern,这意味着只使用 lambda 函数。但是,如果您的目的是创建一个库,并且您的具体类将成为 http 客户端多个方法之上的包装器,然后为用户提供选择,那么它会更好。所以你的混凝土将是包装器。

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

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

客户端/驱动类是:

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

【讨论】:

    【解决方案2】:

    我认为这更像是一个类数组,而不是真正的策略模式。那,你创造的,应该被认为是一种模式吗?我对此表示怀疑,因为 OOP 设计模式主要基于继承来降低代码/设计复杂性。

    您的解决方案需要多一步才能被视为一种模式。因为,此时你的类MyHttpAxios和MyHttpFetch之间没有任何关系。

    您的案例的策略模式结构是什么?根据设计模式的“官方”结构,您的设计应该类似于:

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

    所以,SomeClass 是您实例化策略的地方。 MyHttp 是一个接口(不打算有实例)。 FetchStrategy 和 AxiosStrategy 是您实现代码的类(与您的代码非常相似)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-17
      • 2021-03-15
      • 2011-05-21
      • 1970-01-01
      相关资源
      最近更新 更多