项目之间的类继承 [英] Class inheritance between projects

查看:98
本文介绍了项目之间的类继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在管理小型产品线的用户界面。所有这些产品都是或多或少具有相同模块的硬件,但作为一个单元本身,可能具有额外的全局功能。这些全局功能与这些模块交互,通常为多个模块提供相同的功能。我正在努力维护具有逻辑类继承的OOP方法,但有时似乎不可能不必复制粘贴某些功能。

I'm managing the UI for a small product line. All of these products are hardware that more or less have the same modules, but as a unit themselves, may have extra global features. These global features interact with these modules, often providing multiple modules the same features. I am trying hard to maintain an OOP approach with logical class inheritance, but sometimes it seems impossible to not have to copy-paste some functionality.

在这个例子中,我用 LightSwitch 来说明这种情况。 产品A 产品B 都可以拥有 LightSwitch 产品B 还有一个 AlarmSwitch 产品B 实施 LightSwitch AlarmSwitch 还需要计时器 完全相同的功能产品A 不支持计时器。

In this example, I illustrate the case with LightSwitch. Both Product A and Product B can have a LightSwitch. Product B also has an AlarmSwitch. Product B's implementation of LightSwitch and AlarmSwitch also require a timer with exactly the same functionality. Product A does not support a timer.

我在这里看到的唯一解决方案是跨模块复制粘贴。在没有破坏模型的情况下,我想不出任何其他方法来扩展它。

The only solution I see here is to copy-paste across modules. I can't think of any other way to extend this without breaking the model.

// Common components

class Switch {
  constructor() {
    this.state = false;
  }
  open() {
    this.state = true;
  }
  close() {
    this.state = false;
  }
}

class LightSwitch extends Switch {
  constructor() {
    super();
    this.colour = "white";
  }
  changeColour(colour) {
    this.colour = colour;
  }
}

// Product A 

class LedSwitch extends LightSwitch {
  constructor() {
    super();
    this.blink = false;
  }
  blink(state) {
    this.blink = state;
  }
}

// Product B
// AlarmSwitch and ProductBLightSwitch both need a "timer"

class AlarmSwitch extends Switch {
  constructor() {
    super();
    this.sound = "alarm.wav";
    this.timer = 5000;
  }
  changeSound(sound) {
    this.sound = sound;
  }
  resetTimer() {
    this.timer = 0;
  }
}

class ProductBLightSwitch extends LightSwitch {
  costructor() {
    super();
    this.timer = 5000;
  }
  resetTimer() {
    this.timer = 0;
  }
}


推荐答案

一个可能会考虑将 //公共组件重构并分解为(I)基于函数的mixins / traits,它们带有行为并进入(II)种 hull 只负责类型签名以及将现有代码粘合在一起的类(通过组合和继承代码重用)。同时,人们也可能更关心如何访问/隐藏对象的当前状态。这种方法将通过以下重构的OP示例代码进行演示...

One might consider already refactoring and decomposing the // Common components into (I) function based mixins / traits that carry behavior and into (II) kind of hull classes that are just responsible for a type's signature and for gluing together already existing code (code reuse via composition and inheritance). In the same time one also might care more about how to access/hide an object's current state. Such an approach is going to be demonstrated with the following refactored example code of the OP ...

// Common components

var
  withSerializableTypeProxy = (function () {
    function toString(type) {
      return JSON.stringify(type);
    }
    function valueOf(type) {
      return JSON.parse(JSON.stringify(type))
    }
    let defineProperty = Object.defineProperty;

    return function trait (stateValue) {
      const
        compositeType = this;

      defineProperty(compositeType, 'toString', {
        value: (() => toString(stateValue))
      });
      defineProperty(compositeType, 'valueOf', {
        value: (() => valueOf(stateValue))
      });
    };
  }());


function withChangeableColorProxy(stateValue) {
  const
    compositeType = this;

  compositeType.changeColor = ((colorValue) => stateValue.color = colorValue);

  Object.defineProperty(compositeType, 'color', {
    enumerable: true,
    get: (() => stateValue.color)
  });
  stateValue.color = 'white'; // set default value.
}


class Switch {
  constructor(stateValue) {

    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};
    stateValue.isOpen = true;

    const
      switchType = this;

    withSerializableTypeProxy.call(switchType, stateValue);

    switchType.open = (() => stateValue.isOpen = true);
    switchType.close = (() => stateValue.isOpen = false);

    Object.defineProperty(this, 'isOpen', {
      enumerable: true,
      get: (() => stateValue.isOpen)
    });
  }
}


class LightSwitch extends Switch {
  constructor(stateValue) {
    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};

    super(stateValue);

    withChangeableColorProxy.call(this, stateValue);
  }
}


var
  lightSwitch = (new LightSwitch);

console.log("lightSwitch : ", lightSwitch);

console.log("(lightSwitch instanceof LightSwitch) ? ", (lightSwitch instanceof LightSwitch));
console.log("(lightSwitch instanceof Switch) ? ", (lightSwitch instanceof Switch));

console.log("(lightSwitch + '') : ", (lightSwitch + ''));
console.log("lightSwitch.toString() : ", lightSwitch.toString());
console.log("lightSwitch.valueOf() : ", lightSwitch.valueOf());

console.log("Object.keys(lightSwitch) : ", Object.keys(lightSwitch));

console.log("lightSwitch.isOpen : ", lightSwitch.isOpen);
console.log("lightSwitch.close() : ", lightSwitch.close());
console.log("lightSwitch.isOpen : ", lightSwitch.isOpen);
console.log("lightSwitch.open() : ", lightSwitch.open());
console.log("lightSwitch.isOpen : ", lightSwitch.isOpen);

console.log("lightSwitch.color : ", lightSwitch.color);
console.log("lightSwitch.changeColor('pink') : ", lightSwitch.changeColor('pink'));
console.log("lightSwitch.color : ", lightSwitch.color);
console.log("lightSwitch.changeColor('light-blue') : ", lightSwitch.changeColor('light-blue'));
console.log("lightSwitch.color : ", lightSwitch.color);

.as-console-wrapper { max-height: 100%!important; top: 0; }

然后可以将相同的方法重复应用于 //产品A 及其 LedSwitch 示例,最后是 //产品B的两个示例 AlarmSwitch ProductBLightSwitch ,两者都是OP问题的真正目标。最后的结果可能看起来像......

The very same approach then repeatedly could be applied to // Product A with its LedSwitch example and finally to both examples of // Product B that are AlarmSwitch and ProductBLightSwitch with both being the real target of the OP's question. The final result then might look like that ...

// Common components

var
  withSerializableTypeProxy = (function () {
    function toString(type) {
      return JSON.stringify(type);
    }
    function valueOf(type) {
      return JSON.parse(JSON.stringify(type))
    }
    let defineProperty = Object.defineProperty;

    return function trait (stateValue) {
      const
        compositeType = this;

      defineProperty(compositeType, 'toString', {
        value: (() => toString(stateValue))
      });
      defineProperty(compositeType, 'valueOf', {
        value: (() => valueOf(stateValue))
      });
    };
  }());


function withChangeableColorProxy(stateValue) {
  const
    compositeType = this;

  compositeType.changeColor = ((colorValue) => stateValue.color = colorValue);

  Object.defineProperty(compositeType, 'color', {
    enumerable: true,
    get: (() => stateValue.color)
  });
  stateValue.color = 'white'; // set default value.
}


class Switch {
  constructor(stateValue) {

    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};
    stateValue.isOpen = true;

    const
      switchType = this;

    withSerializableTypeProxy.call(switchType, stateValue);

    switchType.open = (() => stateValue.isOpen = true);
    switchType.close = (() => stateValue.isOpen = false);

    Object.defineProperty(this, 'isOpen', {
      enumerable: true,
      get: (() => stateValue.isOpen)
    });
  }
}


class LightSwitch extends Switch {
  constructor(stateValue) {
    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};

    super(stateValue);

    withChangeableColorProxy.call(this, stateValue);
  }
}


// Product A

var
  withBlinkBehaviorProxy = (function () {
    function setBlinkValue(stateValue, blinkValue) {
      return stateValue.blink = blinkValue;
    }

    return function trait (stateValue) {
      const
        compositeType = this;

      Object.defineProperty(compositeType, 'blink', {
        enumerable: true,
        get: (() => stateValue.blink),
        set: ((blinkValue) => setBlinkValue(stateValue, blinkValue))
      });
      setBlinkValue(stateValue, false); // set default value.
    };
  }());


class LedSwitch extends LightSwitch {
  constructor(stateValue) {
    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};

    super(stateValue);

    withBlinkBehaviorProxy.call(this, stateValue);
  }
}


var
  ledSwitch = (new LedSwitch);

console.log("ledSwitch : ", ledSwitch);

console.log("(ledSwitch instanceof LedSwitch) ? ", (ledSwitch instanceof LedSwitch));
console.log("(ledSwitch instanceof LightSwitch) ? ", (ledSwitch instanceof LightSwitch));
console.log("(ledSwitch instanceof Switch) ? ", (ledSwitch instanceof Switch));

console.log("ledSwitch.valueOf() : ", ledSwitch.valueOf());
console.log("Object.keys(ledSwitch) : ", Object.keys(ledSwitch));

console.log("(ledSwitch.blink = 'blink') : ", (ledSwitch.blink = 'blink'));
console.log("ledSwitch.blink : ", ledSwitch.blink);
console.log("(ledSwitch.blink = true) : ", (ledSwitch.blink = true));
console.log("ledSwitch.blink : ", ledSwitch.blink);


// Product B
// AlarmSwitch and ProductBLightSwitch both need a "timer"

function withPlayerBehaviorProxy(stateValue) {
  const
    compositeType = this;

  compositeType.changeSound = ((soundValue) => stateValue.sound = soundValue);

  Object.defineProperty(compositeType, 'sound', {
    enumerable: true,
    get: (() => stateValue.sound)
  });
  stateValue.sound = 'alarm.wav'; // set default value.
}

function withTimerBehaviorProxy(stateValue) {
  const
    compositeType = this;

  compositeType.resetTimer = (() => stateValue.timer = 0);

  Object.defineProperty(compositeType, 'timer', {
    enumerable: true,
    get: (() => stateValue.timer)
  });
  stateValue.timer = 5000; // set default value.
}


class AlarmSwitch extends Switch {
  constructor(stateValue) {
    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};

    super(stateValue);

    withPlayerBehaviorProxy.call(this, stateValue);
    withTimerBehaviorProxy.call(this, stateValue);
  }
}

class ProductBLightSwitch extends LightSwitch {
  constructor(stateValue) {
    stateValue = ((stateValue != null) && (typeof stateValue == 'object') && stateValue) || {};

    super(stateValue);

    withTimerBehaviorProxy.call(this, stateValue);
  }
}


var
  lightSwitch_B = (new ProductBLightSwitch),
  alarmSwitch = (new AlarmSwitch);

console.log("lightSwitch_B : ", lightSwitch_B);
console.log("alarmSwitch : ", alarmSwitch);

console.log("(lightSwitch_B instanceof ProductBLightSwitch) ? ", (lightSwitch_B instanceof ProductBLightSwitch));
console.log("(alarmSwitch instanceof AlarmSwitch) ? ", (alarmSwitch instanceof AlarmSwitch));

console.log("(lightSwitch_B instanceof LightSwitch) ? ", (lightSwitch_B instanceof LightSwitch));
console.log("(alarmSwitch instanceof LightSwitch) ? ", (alarmSwitch instanceof LightSwitch));

console.log("(lightSwitch_B instanceof Switch) ? ", (lightSwitch_B instanceof Switch));
console.log("(alarmSwitch instanceof Switch) ? ", (alarmSwitch instanceof Switch));

console.log("lightSwitch_B.valueOf() : ", lightSwitch_B.valueOf());
console.log("alarmSwitch.valueOf() : ", alarmSwitch.valueOf());

console.log("Object.keys(lightSwitch_B) : ", Object.keys(lightSwitch_B));
console.log("Object.keys(alarmSwitch) : ", Object.keys(alarmSwitch));

console.log("lightSwitch_B.resetTimer() : ", lightSwitch_B.resetTimer());
console.log("lightSwitch_B.timer : ", lightSwitch_B.timer);

console.log("alarmSwitch.changeSound('ringtone.wav') : ", alarmSwitch.changeSound('ringtone.wav'));
console.log("alarmSwitch.sound : ", alarmSwitch.sound);

.as-console-wrapper { max-height: 100%!important; top: 0; }

这篇关于项目之间的类继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆