合并类似功能DRY

时间:2015-05-31 02:51:03

标签: javascript dry

我有两个非常相似的功能。我想过使用字符串和eval('Man'),但如果可能的话,我很乐意避免使用它。如何抽象这两个,所以它只有一个功能?

function showMan() {
    widget.classList.add(toggle);
    fx.wipeOut({
        node: calWoman
    }).play();
    fx.wipeOut({
        node: pointsWoman
    }).play();

    pointsMan.classList.remove(toggle);
    fx.wipeIn({
        node: calMan
    }).play();
    fx.wipeIn({
        node: pointsMan
    }).play();
    mtFields();
    inputMan.focus();
}

function showWoman() {
    widget.classList.add(toggle);
    fx.wipeOut({
        node: calMan
    }).play();
    fx.wipeOut({
        node: pointsMan
    }).play();

    pointsWoman.classList.remove(toggle);
    fx.wipeIn({
        node: calWoman
    }).play();
    fx.wipeIn({
        node: pointsWoman
    }).play();
    mtFields();
    inputWoman.focus();
}
/** @todo abstract and create one function - showPerson('Man') */

5 个答案:

答案 0 :(得分:4)

如果将所有变量合并到一个对象中会更容易。

function showPerson(sex) {
    var people = {
        woman: {
            cal: calWoman,
            points: pointsWoman,
            input: inputWoman
        },
        man: {
            cal: calMan,
            points: pointsMan,
            input: inputMan
        }
    };

    sex = sex.toLowerCase();
    var otherSex = sex === 'man' ? 'woman' : 'man';  
    var show = people[sex];
    var hide = people[otherSex]

    widget.classList.add(toggle);
    fx.wipeOut({
        node: hide.cal
    }).play();
    fx.wipeOut({
        node: hide.points
    }).play();

    show.points.classList.remove(toggle);
    fx.wipeIn({
        node: show.cal
    }).play();
    fx.wipeIn({
        node: show.points
    }).play();
    mtFields();

    show.input.focus();
}

// usage
showPerson('man');
showPerson('woman');

答案 1 :(得分:1)

创建一个函数showPerson,该函数接受gender作为参数。

从那里,你可以创建一个对象,在其中封装性别特定的函数,然后引用它的对立面:

function showPerson(gender) {
    var optionsObj = {
        man: {
            cal: calMan,
            points: pointsMan,
            input: inputMan.focus,
        },
        woman: {
            cal: calWoman,
            points: pointsWoman,
            input: inputWoman.focus,
        }
    }

    gender === 'man' ? optionsObj[gender].opposite = optionsObj.woman : optionsObj[gender].opposite = optionsObj.man;

    widget.classList.add(toggle)
    fx.wipeOut({
        node: optionsObj[gender].cal,
    }).play();
    fx.wipeOut({
        node: optionsObj[gender].points
    }).play();

    optionsObj[gender].points.classList.remove(toggle);
    fx.wipeIn({
        node: optionsObj[gender].opposite.cal
    }).play();
    fx.wipeIn({
        node: optionsObj[gender].opposite.points
    }).play();
    mtFields();
    optionsObj[gender].input()
}

答案 2 :(得分:1)

考虑到这里所有内容的顺序,这有点令人困惑,但诀窍不是将两个巨大的功能分解为一个,而是将系统分解成可以更好地组合的较小部分。

function hideNode ( node ) { fx.wipeOut({ node: node }).play(); }
function revealNode ( node ) { fx.wipeIn({ node: node }).play(); }

function showPerson (removalList, revealList, points, input) {
  widget.classList.add(toggle);
  removalList.forEach(hideNode);
  points.classList.remove(toggle);
  revealList.forEach(revealNode);
  input.focus();
}

// somewhere NORTH of your current function
var womanList = [calWoman, pointsWoman];
var manList = [calMan, pointsMan];
var revealList, removalList, input, points;

if (man) {
   removalList = womanList;
   revealList = manList;
   input = inputMan;
   points = pointsMan;
} else {
   removalList = manList;
   revealList = womanList;
   input = inputWoman;
   points = pointsWoman;
}

// alternatively, these can be stored in an object this way, and pulled by a flag, rather than by an if statement...

showPerson( removalList, revealList, points, input );

如果你注意到,逻辑依旧非常简单,依赖于将参数传递给我的函数 选择值的逻辑也很简单,但我已经不再需要在函数之外选择;这是其他人的关注(即使它是一个新功能,除了选择并传递适当的值之外什么都不做)。

我已经在函数中执行了重复步骤,并将它们分解为更小的函数,并且我正在使用列表(甚至只是两个列表),因此我可以忽略调用某些内容的详细信息......它和你发给我的人完全一样被打电话 我还没有解决“谁叫showPerson,有什么和什么访问......”,但这是一个更具体的问题需要解决;从这里开始包装任何使用if () { }和值设置/传递的人都很快。

这远不是一个干净的成品,但它提供的提供的是如何在更大的项目中处理这些移动部件。

修改

为了更进一步,并且为了表明你不需要在重写方面做太多,这里(至少不是最初的),我将进一步增加:

function getPersonConfig ( type ) {
  var config = {
    man: { /* ... */ },
    woman: { /* ... */ }
  };

  return config[type];
}

function showMan () {
  var config = getPersonConfig( "man" );
  showPerson( config );
}

function showWoman () {
  var config = getPersonConfig( "woman" );
  showPerson( config );
}

我正在使用 稍微 不同版本的showPerson功能...
在其中,我传递了一个对象(而不是单独的变量),但这就是我需要改变的所有内容。

看看这些小巧,可组合的功能给我带来了什么...... 您根本不必更改showManshowWoman 相反,你正在劫持他们曾经的东西,并使用它们来调用执行相同工作的较小函数,但现在更加可分离和可重用。

这是一个“外观”或“贴面”,通常在工程中,它是一个被讨论的API或服务,但它基本上意味着当你想要更新代码时,如果你可以保留方法名称,输入和输出以及所有后果相同(来自外部世界可以告诉),然后您可以根据需要更改代码的内部结构。

showMan仍然被调用,现在它正在做的事情仍在进行中,没有任何添加或删除。
showWoman相同。
所以你已经成功地保留了一个立面,同时取代了内部。

外墙也可以走另一条路;以您希望它向外界展示的方式编写新的服务/ API,并将其发布到世界上,只需将旧代码挂钩到新结构,然后随时填写新功能

希望这有帮助。

答案 3 :(得分:0)

function showPerson(calPerson, pointsPerson, inputPerson) {
   //replace calWoman and calMan with calPerson

   //replace pointsMan and pointsWoman with pointsPerson

   //replace inputMan and inputWoman with inputPerson

}

如果你无法传递它,可以使用闭包来初始化带有这些引用的函数。

function showPerson(calPerson, pointsPerson, inputPerson) {
   function showingPersonWithReferences() {
     //replace calWoman and calMan with calPerson

     //replace pointsMan and pointsWoman with pointsPerson

     //replace inputMan and inputWoman with inputPerson
   }

   return showingPersonWithReferences;
}

这是javascript中的一些函数式编程。

答案 4 :(得分:0)

尽管上面提供了出色的解决方案和建议,但我最终还是做了这个,这对我的情况最有效(最少改写,大多数干预)。非常感谢您回答这个问题。



function showPerson() {
  /**
   * the element being hidden should be placed in the first parameter
   * @param {object} woman - HTMLFormElement (pointsWoman)
   * @param {object} man - HTMLFormElement (pointsMan)
   * @example showPerson(pointsWoman, pointsMan);
   */

  if (arguments[0] instanceof HTMLFormElement && arguments[1] instanceof HTMLFormElement) {
    widget.classList.add(toggle);

    fx.wipeOut({
      node: arguments[0]
    }).play();

    arguments[1].classList.remove(toggle);

    fx.wipeIn({
      node: arguments[1]
    }).play();

    mtFields();
    arguments[1][0].focus();
  }
  else {
    throw new Error('You need to provide the forms to show/hide');
  }
}