我在以允许我通过数据的通用描述符或特征引用数据的方式对数据进行编目时遇到问题。我很清楚继承、特征(编程概念)和接口(interface),但这些似乎都不是我问题的正确答案。
我正在用 JavaScript 编写一个程序,其中可能包含许多不同的项或对象。假设我有一个 WoodenShortSword
数据类型,我想表达它具有 Flammable
和 Weapon
以及 OneHanded
。然后,我想定义一个函数,该函数仅将 OneHanded
和 Weapon
对象作为参数。或者,也许只有 Flammable
和 Wearable
的对象,或者 Flammable
但不是 Weapon
.
我该怎么做?
到目前为止,我已经了解了 JavaScript 和 TypeScript 中的继承,这在技术上是可行的,但由于不允许多重继承,因此需要一堆中间类。像 FlammableWeapon
或 OneHandedWeapon
。这很麻烦而且不理想。
我查看了 TypeScript 的抽象类和接口(interface),但它们更多的是关于共享功能,而不是描述事物。并且没有内置方法可以让我在运行时检查对象是否满足接口(interface)。
我还查看了 tcomb图书馆。尽管像我描述的那样的系统是可能的,但它仍然非常麻烦且容易出错。
最佳答案
如果@Manngo 的方法还不是解决方案,可以考虑给予 这个答案需要 10 到 15 分钟的阅读时间。它实现了@Manngo 的方法,但侧重于 关于解决常见的组合冲突,如果涉及到组合的创建 来自有状态混合/特征的类型。
根据 OP 对所需特征的描述,可以很容易地找到 基于函数的 mixin/trait 方法。从而实现细粒度的可组合/可重用 每个单元都描述了一个特定的行为集 不同的(封装的)数据。
一个人会实现某种flammable
和oneHanded
伴随的行为
通过例如Weapon
基类。
但是从上面提到的所有内容组成一个 WoodenShortSword
并不像
正如人们第一眼看到的那样直截了当。可能有方法
来自 oneHanded
和 Weapon
需要对彼此采取行动(封装)
状态例如更新武器的 isActivated
状态,例如
oneHanded
的 takeInLeftHand
方法被调用,或者 visa verce 以防万一
武器的deactivate
Action 发生。然后很高兴得到更新
oneHanded
的内部 isInHand
状态。
一个可靠的方法是方法修改,它必须依赖
在样板代码上,除非有一天 JavaScript 原生实现
Function.prototype[around|before|after|afterReturning|afterThrowing|afterFinally]
。
作为概念证明的更长的示例代码可能看起来像这样......
function withFlammable() { // composable unit of reuse (mixin/trait/talent).
var
defineProperty = Object.defineProperty,
isInFlames = false;
defineProperty(this, 'isFlammable', {
value: true,
enumerable: true
});
defineProperty(this, 'isInFlames', {
get: function () {
return isInFlames;
},
enumerable: true
});
defineProperty(this, 'catchFire', {
value: function catchFire () {
return (isInFlames = true);
},
enumerable: true
});
defineProperty(this, 'extinguish', {
value: function extinguish () {
return (isInFlames = false);
},
enumerable: true
});
}
function withOneHanded() { // composable unit of reuse (mixin/trait/talent).
var
defineProperty = Object.defineProperty,
isInLeftHand = false,
isInRightHand = false;
function isLeftHanded() {
return (isInLeftHand && !isInRightHand);
}
function isRightHanded() {
return (isInRightHand && !isInLeftHand);
}
function isInHand() {
return (isInLeftHand || isInRightHand);
}
function putFromHand() {
return isInHand() ? (isInLeftHand = isInRightHand = false) : (void 0);
}
function takeInLeftHand() {
return !isInLeftHand ? ((isInRightHand = false) || (isInLeftHand = true)) : (void 0);
}
function takeInRightHand() {
return !isInRightHand ? ((isInLeftHand = false) || (isInRightHand = true)) : (void 0);
}
function takeInHand() {
return !isInHand() ? takeInRightHand() : (void 0);
}
function switchHand() {
return (
(isInLeftHand && ((isInLeftHand = false) || (isInRightHand = true)))
|| (isInRightHand && ((isInRightHand = false) || (isInLeftHand = true)))
);
}
defineProperty(this, 'isOneHanded', {
value: true,
enumerable: true
});
defineProperty(this, 'isLeftHanded', {
get: isLeftHanded,
enumerable: true
});
defineProperty(this, 'isRightHanded', {
get: isRightHanded,
enumerable: true
});
defineProperty(this, 'isInHand', {
get: isInHand,
enumerable: true
});
defineProperty(this, 'putFromHand', {
value: putFromHand,
enumerable: true,
writable: true
});
defineProperty(this, 'takeInLeftHand', {
value: takeInLeftHand,
enumerable: true,
writable: true
});
defineProperty(this, 'takeInRightHand', {
value: takeInRightHand,
enumerable: true,
writable: true
});
defineProperty(this, 'takeInHand', {
value: takeInHand,
enumerable: true,
writable: true
});
defineProperty(this, 'switchHand', {
value: switchHand,
enumerable: true
});
}
function withStateCoercion() { // composable unit of reuse (mixin/trait/talent).
var
defineProperty = Object.defineProperty;
defineProperty(this, 'toString', {
value: function toString () {
return JSON.stringify(this);
},
enumerable: true
});
defineProperty(this, 'valueOf', {
value: function valueOf () {
return JSON.parse(this.toString());
},
enumerable: true
});
}
class Weapon { // base type.
constructor() {
var
isActivatedState = false;
function isActivated() {
return isActivatedState;
}
function deactivate() {
return isActivatedState ? (isActivatedState = false) : (void 0);
}
function activate() {
return !isActivatedState ? (isActivatedState = true) : (void 0);
}
var
defineProperty = Object.defineProperty;
defineProperty(this, 'isActivated', {
get: isActivated,
enumerable: true
});
defineProperty(this, 'deactivate', {
value: deactivate,
enumerable: true,
writable: true
});
defineProperty(this, 'activate', {
value: activate,
enumerable: true,
writable: true
});
}
}
class WoodenShortSword extends Weapon { // ... the
constructor() { // inheritance
// part
super(); // ...
withOneHanded.call(this); // ... the
withFlammable.call(this); // composition
// base
withStateCoercion.call(this); // ...
var // ... the method modification block ...
procedWithUnmodifiedDeactivate = this.deactivate,
procedWithUnmodifiedActivate = this.activate,
procedWithUnmodifiedPutFromHand = this.putFromHand,
procedWithUnmodifiedTakeInHand = this.takeInHand,
procedWithUnmodifiedTakeInLeftHand = this.takeInLeftHand,
procedWithUnmodifiedTakeInRightHand = this.takeInRightHand;
this.deactivate = function deactivate () { // "after returning" method modification.
var
returnValue = procedWithUnmodifiedDeactivate();
if (returnValue === false) {
procedWithUnmodifiedPutFromHand();
}
return returnValue;
};
this.activate = function activate () { // "after returning" method modification.
var
returnValue = procedWithUnmodifiedActivate();
if (returnValue === true) {
procedWithUnmodifiedTakeInHand();
}
return returnValue;
};
this.putFromHand = function putFromHand () { // "after returning" method modification.
var
returnValue = procedWithUnmodifiedPutFromHand();
if (returnValue === false) {
procedWithUnmodifiedDeactivate();
}
return returnValue;
};
this.takeInHand = function takeInHand () { // "after returning" method modification.
var
returnValue = procedWithUnmodifiedTakeInHand();
if (returnValue === true) {
procedWithUnmodifiedActivate();
}
return returnValue;
};
this.takeInLeftHand = function takeInLeftHand () { // "before" method modification.
if (!this.isInHand) {
procedWithUnmodifiedActivate();
}
return procedWithUnmodifiedTakeInLeftHand();
};
this.takeInRightHand = function takeInRightHand () { // "before" method modification.
if (!this.isInHand) {
procedWithUnmodifiedActivate();
}
return procedWithUnmodifiedTakeInRightHand();
};
}
}
var
sword = new WoodenShortSword;
console.log('sword : ', sword);
console.log('(sword + "") : ', (sword + ""));
console.log('sword.valueOf() : ', sword.valueOf());
console.log('\n');
console.log('sword.isFlammable : ', sword.isFlammable);
console.log('sword.isInFlames : ', sword.isInFlames);
console.log('\n');
console.log('sword.isOneHanded : ', sword.isOneHanded);
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.deactivate : ', sword.deactivate);
console.log('sword.activate : ', sword.activate);
console.log('\n');
console.log('sword.deactivate() : ', sword.deactivate());
console.log('sword.activate() : ', sword.activate());
console.log('sword.activate() : ', sword.activate());
console.log('\n');
console.log('sword.isFlammable : ', sword.isFlammable);
console.log('sword.isInFlames : ', sword.isInFlames);
console.log('\n');
console.log('sword.isOneHanded : ', sword.isOneHanded);
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.switchHand() : ', sword.switchHand());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.takeInRightHand() : ', sword.takeInRightHand());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.putFromHand() : ', sword.putFromHand());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.takeInLeftHand() : ', sword.takeInLeftHand());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.deactivate() : ', sword.deactivate());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.activate() : ', sword.activate());
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.switchHand() : ', sword.switchHand());
console.log('\n');
console.log('sword.isFlammable : ', sword.isFlammable);
console.log('sword.isInFlames : ', sword.isInFlames);
console.log('\n');
console.log('sword.isLeftHanded : ', sword.isLeftHanded);
console.log('sword.isRightHanded : ', sword.isRightHanded);
console.log('sword.isInHand : ', sword.isInHand);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.catchFire() : ', sword.catchFire());
console.log('\n');
console.log('sword.isFlammable : ', sword.isFlammable);
console.log('sword.isInFlames : ', sword.isInFlames);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.extinguish() : ', sword.extinguish());
console.log('\n');
console.log('sword.isFlammable : ', sword.isFlammable);
console.log('sword.isInFlames : ', sword.isInFlames);
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
console.log('sword.putFromHand() : ', sword.putFromHand());
console.log('\n');
console.log('sword.isActivated : ', sword.isActivated);
console.log('\n');
.as-console-wrapper { max-height: 100%!important; top: 0; }
关于javascript - 如何按共同特征组织数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43404165/