有没有更好的方法来避免if / then / else?

时间:2017-10-24 14:26:35

标签: javascript node.js function if-statement

背景

我们有一个包含信息的请求对象。该特定对象有一个名为partnerId的字段,用于确定我们要对请求执行的操作。

典型的方法是巨大的if / then / else:

function processRequest( request ){
    if( request.partnerId === 1 ){
        //code here
    }else if( request.partnerId === 23 ){
        //code here
    }
    //and so on. This would be a **huge** if then else.
}

这种方法有两个主要问题:

  • 这个功能很大。巨大的功能是代码气味(解释为什么接下来),但主要是它们变得非常难以快速阅读和维护。
  • 这个功能不止一件事。这是个问题。良好的编码实践建议1功能应该只做一件事。

我们的解决方案

为了绕过以前的问题,我挑战我的同事想出一个不同的解决方案,他想出了一个动态构建我们想要使用的函数名称并调用它的函数。听起来很复杂,但这段代码将澄清它:

const functionHolder = {
    const p1 = request => {
        //deals with request
    };

    const p23 = request => {
        //deals with request
    };

    return { p1, p23 }; 
};

const processRequest = request => {
    const partnerId = request.partnerId;
    const result = functionHolder[`p${partnerId}`](request);
    return result;
};

问题

此解决方案优于前一个解决方案:

  1. 如果没有其他的话,没有巨大的巨大功能。
  2. 每个执行路径不是仅执行一项操作的单个函数
  3. 然而,它也存在一些问题:

    1. 我们正在使用一个实际上没用的对象functionHolderp1p23并不共享任何共同点,我们只是使用此对象,因为我们不知道我们如何动态构建函数的名称并将其命名为
    2. 没有else个案。如果我们得到一个不正确的参数代码就会爆炸。
    3. 带有规则non-used-vars的out eslint抱怨p1p23未被使用,我们也不知道如何修复它(https://eslint.org/docs/rules/no-unused-vars)。
    4. 最后一个问题给我们的印象是,这个解决方案可能不是那么好。也许这种模式可以避免如果其他的话还有一些我们尚未发现的邪恶。

      问题

      1. 我们可以使用任何其他模式来避免巨大的if then else语句(或切换案例)吗?
      2. 有没有办法摆脱functionHolder对象?
      3. 我们应该更改模式还是修改规则?
      4. 期待任何反馈!

2 个答案:

答案 0 :(得分:1)

你可以通过从不首先声明它们来摆脱未使用的变量:

const functionHolder = {
    p1: request => {
        //deals with request
    },
    p23: request => {
        //deals with request
    };
};

const processRequest = request => {
    const partnerId = request.partnerId;
    const method = functionHolder[`p${partnerId}`]
    if(method)   // Is there a method for `partnerId`?
        return method(request);
    return null; // No method found. Return `null` or call your default handler here.
};

回答你的观点:

  1. Yeap,如上所示。
  2. 不是没有某种对象。
  3. 这取决于你。无论你喜欢什么。

答案 1 :(得分:0)

也许我没有正确理解这个问题,但为什么不能成为持有这些方法的对象?

const functionHolder = {
  1: function(request) {
    // do something
  },
  23: function(request) {
    // do something
  },
  _default: function(request) {
    // do something
  }
}

function processRequest(request) {
  (functionHolder[request.partnerId] || functionHolder._default)(request)
}

说明:

  1. 对象functionHolder包含用于处理给定请求的每种方法。
  2. functionHolder的键(例如1)将直接对应request.partnerId的值,这些成员的值是适当的方法。< / LI>
  3. 功能processRequest&#34;选择&#34; functionHolder中的适当方法(即object[key]),并将此方法作为参数调用此方法(即method(parameter))。
  4. 如果_default与任何现有密钥不匹配,我们在密钥request.partnerId下也有默认方法。鉴于a || b;如果a是&#34; falsy&#34;,在这种情况下undefined(因为没有对象的相应成员),请评估为b
  5. 如果您担心functionHolder&#34;臃肿&#34;,那么您可以将每种方法分开:

    const p1 = request => {
      // do something
    }
    
    const p23 = request => {
      // do something
    }
    
    const _default = request => {
      // do something
    }
    

    然后将它们组合成一个&#34;摘要&#34;各种各样的对象。

    const functionHolder = {
      1: p1,
      23: p23,
      _default: _default
    }
    

    processRequest与上述相同。

    这会增加很多全局变量。

    另一个优点是您可以动态导入/更改/声明方法。 e.g。

    functionHolder[1] = p1b // where p1b is another request handler (function) for `request.partnerId` = 1
    functionHolder[5] = p5 // where p5 is a request handler (function) that has not yet been declared for `request.partnerId` = 5
    

    结合上述内容,无需声明许多全局变量,同时还能够分离每种方法的声明:

    const functionHolder = {}
    
    functionHolder._default = request => {
      // do something
    }
    
    functionHolder[1] = request => {
      // do something
    }
    
    functionHolder[23] = request => {
      // do something
    }
    

    processRequest与上述相同。

    你必须确保这些方法已经加载到&#34;在致电functionHolder之前致processRequest