从Mixin到对象组合

16-07-23 banq
         

Facebook提出Mixin的三个问题:

1.缺乏封装

2.隐式依赖

3.名称冲突

下面是Javascript的实现Mixin的类:

const Coloured = {
  // __Public Methods__
  setColourRGB ({r, g, b}) {
    return this.colourCode = {r, g, b};
  },
  getColourRGB () {
    return this.colourCode;
  },
  getColourHex () {
    return this.rgbToHex(this.colourCode);
  },

  // __Private Methods__
  componentToHex(c) {
    const hex = c.toString(16);

    return hex.length == 1 ? "0" + hex : hex;
  },
  rgbToHex({r, g, b}) {
    return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b);
  }
};

class Todo {
  constructor (name) {
    this.name = name || 'Untitled';
    this.done = false;
  }
  title () {
    return name;
  }
  do () {
    this.done = true;
    return this;
  }
  undo () {
    this.done = false;
    return this;
  }
}

Object.assign(Todo.prototype, Coloured);
<p>

注意到上述代码最后一行是将Todo和Coloured两个类Mixin混合。

这种Mixin混合显然破坏了Todo的封装性,因为将Coloured的行为混合编织进入了Todo,我们通过打开Todo代码是无法知道原来在运行时Todo还会依赖Coloured,产生了隐形依赖。

通过组合方式可以替代Mixn:

class Todo {
  constructor (name) {
    this.name = name || 'Untitled';
    this.done = false;
    this[colouredObject] = Object.assign({}, Coloured);
  }
  do () {
    this.done = true;
    return this;
  }
  undo () {
    this.done = false;
    return this;
  }
  setColourRGB ({r, g, b}) {
    return this[colouredObject].setColourRGB({r, g, b});
  }
  getColourRGB () {
    return this[colouredObject].getColourRGB();
  }
  getColourHex () {
    return this[colouredObject].getColourHex();
  }
}
<p>

在上面代码中,我们实际将在外面的Object.assign类的混合动作从外面迁移到Todo这个类里面。

其实搞这么复杂,不如老老实实地直接在Todo中直接引用Coloured,反而将两者依赖直观表达出来。

当然,原文在这个复杂道路上进行了疯狂探险:

From Mixins to Object Composition