问题描述
我在对数据进行分类时遇到了麻烦,该方法使我无法通过其公共描述符或特征来引用数据.我很了解继承,特征(编程概念)和接口,但是这些似乎都不是解决我问题的正确方法.
I'm having trouble cataloging data in a way that allows me to reference data by its common descriptors or traits. I'm well aware of inheritance, traits (the programming concept), and interfaces, but none of those seems to be the right answer to my problem.
我正在用JavaScript编写一个程序,该程序可能具有许多不同的项目或对象.假设我的数据类型为WoodenShortSword
,我想表示它具有Flammable
,Weapon
和OneHanded
的特征.然后,我想定义一个仅将同时为OneHanded
和Weapon
的对象作为参数的函数.或者,也许只有Flammable
和Wearable
的对象,或者Flammable
和不是一个Weapon
的对象.
I'm writing a program in JavaScript that has potentially many different items or objects. Let's say I have a datatype of WoodenShortSword
and I want to express that it has the traits of being Flammable
and Weapon
and OneHanded
. Then, I want to define a function that takes as an argument only objects that are both OneHanded
and Weapon
. Or, perhaps, only objects that are Flammable
and Wearable
, or Flammable
and not a Weapon
.
我该怎么做?
到目前为止,我已经研究了JavaScript和TypeScript中的继承,从技术上讲,继承是可行的,但是由于不允许多重继承,因此需要一堆中间类.类似于FlammableWeapon
或OneHandedWeapon
.这很麻烦,也不理想.
So far, I've looked at inheritance in JavaScript and TypeScript, which would technically work, but would require a bunch of intermediate classes since multiple inheritance isn't allowed. Like FlammableWeapon
or OneHandedWeapon
. That's cumbersome and not ideal.
我看了TypeScript的抽象类和接口,但更多的是关于共享功能,而不是描述事物.而且,我无法看到内置的方法来检查对象在运行时是否满足接口要求.
I looked at TypeScript's abstract classes and interfaces, but those are more about sharing functionality, not describing things. And there's no built-in way that I could see to check if an object satisfies an interface at runtime.
我还查看了 tcomb 库.尽管像我正在描述的系统是可行的,但它仍然非常麻烦且容易出错.
I also looked at the tcomb library. Although a system like I'm describing is possible, it's still very cumbersome and error-prone.
推荐答案
如果@Manngo的方法还不是解决方案,则可以考虑提供这个答案需要10到15分钟的阅读时间.它实现了@Manngo的方法,但着重于解决涉及合成的共同构图冲突有状态的mixins/特征中的类型.
If @Manngo 's approach is not already the solution, one might consider givingthis answer a 10 to 15 min read. It implements @Manngo 's approach but focuseson solving common composition conflicts if it comes to creation of compositetypes from stateful mixins/traits.
按照OP对所需性状的描述,可以轻松地获得一个基于功能的混合/特征方法.从而实现细粒度的可组合/可重用每个单元都描述了一个特定的行为集,这些行为集可以独立发挥作用,不同的(封装的)数据.
Following the OP's description of the desired traits, one easily could go fora function based mixin/trait approach. Thus implementing fine grained composable/reusableunits that each describe a specific behavioral set that acts upon its own anddistinct (encapsulated) data.
一个人会实现某种flammable
和oneHanded
行为通过例如Weapon
基类.
One would implement some kind of flammable
and oneHanded
behavior accompaniedby e.g. a Weapon
base class.
但是由上述所有内容组成WoodenShortSword
并不像乍一看像人们所期望的那样简单明了.可能有方法来自oneHanded
和Weapon
的彼此需要采取措施的(封装的)例如的状态尽快更新武器的isActivated
状态,例如oneHanded
的takeInLeftHand
方法被调用,或者万一发生签证签证武器的deactivate
动作发生.然后很高兴得到更新oneHanded
的内部isInHand
状态.
But composing a WoodenShortSword
from all of the above mentioned is not asstraightforward as one might expect at first sight. There might be methodsfrom oneHanded
and Weapon
that need to take action on each others (encapsulated)state for e.g. updating a weapon's isActivated
state as soon as an e.g.takeInLeftHand
method of oneHanded
gets invoked, or visa verce in casea weapon's deactivate
action takes place. It then was nice getting updatedthe inner isInHand
state of oneHanded
.
一种可靠的方法是方法修改,该方法必须依靠样板代码上的代码,除非有一天JavaScript本身实现Function.prototype[around|before|after|afterReturning|afterThrowing|afterFinally]
.
A reliable approach for this is method modification that has to relyon boilerplate code, unless JavaScript at one day natively implementsFunction.prototype[around|before|after|afterReturning|afterThrowing|afterFinally]
.
再有一个更长的示例代码作为概念证明,可能看起来像这样……
A longer example code as proof of concept then might look like this one ...
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; }
这篇关于如何按共同特征组织数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!