我在以一种允许我通过其共同描述符或特征引用数据的方式编目数据时遇到了麻烦。我非常了解继承,特征(编程概念)和接口,但这些似乎都不是我问题的正确答案。
我在JavaScript中编写一个程序,可能有许多不同的项目或对象。我们假设我的数据类型为WoodenShortSword
,我想表达它的特征是Flammable
和Weapon
以及OneHanded
。然后,我想定义一个函数,它只将OneHanded
和Weapon
的对象作为参数。或者,也许只有Flammable
和Wearable
,或Flammable
和不1>} Weapon
的对象。
我该怎么做?
到目前为止,我已经研究过JavaScript和TypeScript中的继承,这在技术上可行,但是由于不允许多重继承,因此需要一堆中间类。与FlammableWeapon
或OneHandedWeapon
一样。这很麻烦而且不理想。
我查看了TypeScript的抽象类和接口,但这些更多是关于共享功能,而不是描述事物。并且没有内置的方法可以检查对象是否在运行时满足接口。
我还查看了tcomb库。尽管像我这样描述的系统是可能的,但它仍然非常麻烦且容易出错。
答案 0 :(得分:1)
如果@Manngo的方法还不是解决方案,那么可以考虑给予 这个回答是10到15分钟的阅读。它实现了@Manngo的方法但重点突出 如果涉及复合材料的创建,解决常见的构图冲突 来自有状态混合/特征的类型。
根据OP对所需特征的描述,可以轻松地找到 基于函数的mixin / trait方法。从而实现细粒度可组合/可重复使用 每个单元描述一个特定的行为集,它自己和它自己的行为 不同的(封装的)数据。
可以实现某种flammable
和oneHanded
行为
通过例如一个Weapon
基类。
但是,从上面提到的所有内容中编写WoodenShortSword
并不如此
人们可能会一见钟情直截了当。可能有方法
来自oneHanded
和Weapon
需要对彼此采取行动(封装)
例如,国家尽快更新武器的isActivated
州
takeInLeftHand
的{{1}}方法被调用,或签署verce以防万一
武器的oneHanded
动作发生了。很高兴得到更新
deactivate
的内部isInHand
状态。
可靠的方法是方法修改必须依赖
在样板代码上,除非JavaScript在某一天本地实现
oneHanded
。
作为概念证明的较长示例代码可能看起来像这样......
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');
答案 1 :(得分:0)
JavaScript对象是可扩展的,因此thing.Flammable=true
之类的表达式是有效的,并且可以正常工作。
要测试对象是否具有属性,可以使用thing.hasOwnProperty('property')
。这比事物中的'property
更好,因为后者将包括原型链。
然后,函数可以按如下方式工作:
function doit(object) {
if(!object.hasOwnProperty('Flammable') return;
// etc
}
这样,对象可以具有多个特征,而不会因伪造多重继承而烦恼。