项目之间的类继承 [英] Class inheritance between projects
问题描述
我正在管理小型产品线的用户界面。所有这些产品都是或多或少具有相同模块的硬件,但作为一个单元本身,可能具有额外的全局功能。这些全局功能与这些模块交互,通常为多个模块提供相同的功能。我正在努力维护具有逻辑类继承的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屋!