quotes

We have two configuration objects (config1, config2), and now the requirement is that we need to merge the two objects. We require different combinations for different fields in the object. The result of the synthesis is expected as shown in the following example. For the data field in the object, the url for the data in our merged object comes from Config2; For the headers field of the object, we use deepMerge, that is, if config2.headers exists, select the field in config2.headers; otherwise, select the field in config2.headers. For other fields, we default to config2.

In fact, the above requirement can be summarized in one sentence: choose different merge strategies based on different fields of the object.

const config1 = {
  method: 'get'.data: {
    a: 1
  },
  headers: {
    'Content-Type': 'application/json'}}const config2 = {
  url: '/config/post'.data: {
    a: 10.b: 100
  },
  headers: {
    Accept: 'application/json, text/plain, */*'}}const expected = {
  method: 'get'.url: '/config/post'.data: {
    a: 10.b: 100
  },
  headers: {
    'Content-Type': 'application/json'.Accept: 'application/json, text/plain, */*'}}Copy the code

implementation

  1. Develop a strategy. Create different policies based on different requirements.
  2. Set policy rules. Obtain corresponding policies according to the rules.
  3. Applications.
interface Strategy {
  (value1: any.value2: any) :any
}

interface StrategyMap {
  [index: string]: Strategy
}

// 1. Formulate a strategy.

// Default policy
const defaultStrategy: Strategy = (value1: any.value2: any) :any= > {}

// Only the value2 policy is selected.
const fromValue2Strategy: Strategy = (value1: any.value2: any) :any= > {}

// Deep merge strategy.
const deepMergeStrategy: Strategy = (value1: any.value2: any) :any= > {}

// 2. Set policy rules.
const strategyMap: StrategyMap = {}
const fromValuesKeys = ['data'.'url']
const deepMergeKeys = ['headers']
// const defaultKeys: any[]

fromValuesKeys.forEach((key) = > (strategyMap[key] = fromValue2Strategy))
deepMergeKeys.forEach((key) = > (strategyMap[key] = deepMergeStrategy))

/ / 3. Application
function merge(config1: any, config2: any) {
  const dest: any = {}
  Object.keys(config2).forEach((key: string) = > {
    // Get the policy
    const strategy: Strategy = strategyMap[key] || defaultStrategy
    // Merge the configuration
    dest[key] = strategy(config1[key], config2[key])
  })

  Object.keys(config1).forEach((key: string) = > {
    const strategy: Strategy = strategyMap[key] || defaultStrategy

    dest[key] = strategy(config1[key], config2[key])
  })
  return dest
}
Copy the code
  1. As needed, we createddefaultStrategy.fromValue2Strategy.deepMergeStrategyThree strategies. Each policy corresponds to a different merge rule.
  2. usingMapIs used for policy matching.
  3. Applications.

Let’s implement the specific merge rule:

// Default policy
const defaultStrategy: Strategy = (value1: any.value2: any) :any= > {
  return typeofvalue2 ! = ='undefined' ? value2 : value1
}

// Only the value2 policy is selected.
const fromValue2Strategy: Strategy = (value1: any.value2: any) :any= > {
  return value2
}

// Deep merge strategy.
const isPlainObject = (value: any) = >
  Object.prototype.toString.call(value) === '[object Object]'

/** * deep merge *@param Values Set of objects to be merged */
const _deepMerge = (. values:any[]) = > {
  const result = Object.create(null)
  values.forEach((obj) = > {
    if (typeof obj === 'undefined' || obj === null) {
      return
    }
    Object.keys(obj).forEach((key) = > {
      const value = obj[key]
      if (isPlainObject(value)) {
        result[key] = isPlainObject(result[key])
          ? _deepMerge(result[key], value)
          : _deepMerge(Object.create(null), value)
      } else {
        result[key] = value
      }
    })
  })
  return result
}

const deepMergeStrategy: Strategy = (value1: any.value2: any) :any= > {
  if (isPlainObject(value2)) {
    return _deepMerge(value1, value2)
  } else if (typeofvalue2 ! = ='undefined') {
    return value2
  } else if (isPlainObject(value1)) {
    return _deepMerge(value1)
  } else if (typeofvalue1 ! = ='undefined') {
    return value1
  }
}
Copy the code

summary

Policy pattern provides a flexible application, using polymorphic implementation to obtain different strategies, according to different strategies to get different results. In the front end axios, merge configuration is implemented using the policy pattern.

We can abstract the policy pattern into the following code for subsequent use:

// Declare the policy interface
interface Strategy {}
interface StrategyMap {
  [index: string]: Strategy
}

// Mapping rule
const strategyMap: StrategyMap = {}
strategyMap[key] = ConcreteStrategy // ConcreteStrategy needs to provide concrete implementations.

// Use the policy
function use(strategyName: string) {
  const strategy = strategyMap[strategyName]
  // do something...
  strategy()
}
Copy the code