我目前正在开发一个庞大的javascript项目,该项目具有庞大的类层次结构,并且大量使用mixins来扩展基类的功能。以下是mixin的示例,我们使用compose library创建类似对象:
// Base.js
var Base = compose({
setX: function (x) {
this.x = x;
},
setY: function (y) {
this.y = y;
},
setPosition: function (x, y) {
this.setX(x);
this.setY(y);
}
})
// SameXAndY.js - mixin
var SameXAndY = compose({
// Executes after setX in Base.js
setX: compose.after(function (x) {
this.y = x;
}),
// Executes after setY in Base.js
setY: compose.after(function (y) {
this.x = y;
}),
// Overrides setPosition in Base.js
setPosition: compose.around(function (base) {
return function (x, y) {
if (x !== y) {
throw 'x !== y';
}
return base.call(this, x, y);
}
})
})
此解决方案存在以下问题:
我正在寻找更好的类似插件的替代品,这些替代品可以根据以下要求逐步重构所有现有的mixin:
PluginA
需要PluginB
和PluginC
)。SameXAndY
)。我知道没有" easy"回答我的问题,但我真的很想听听你对这个话题的看法。设计模式名称,相关博客文章,甚至链接到源代码将不胜感激。
干杯, 弗拉基米尔。
答案 0 :(得分:1)
我确实重构了OP的例子,它做了所要求的。 如果我出错了,请告诉我。
function loop(max, callback, i) {
i = i || 0;
if (i < max) {
console.log("loop count: " + i);
return callback(i).then(function() {
return loop(max, callback, ++i);
});
}
}
function doAsyncStuff(passedNum) {
return new Promise(function(resolve, reject) {
// Fake asynchronous stuff for 500ms using setTimeout.
// Put your real async code here, calling "resolve" when
// it's finished.
setTimeout(function() {
console.log("callback count: " + passedNum);
resolve();
}, 500);
});
}
loop(5, doAsyncStuff).then(function() {
console.log('All done looping!')
});
class BaseXYType {
constructor(stateValue) { // injected state object.
this.setX = function setX (x) {
return (stateValue.x = x);
};
this.setY = function setY (y) {
return (stateValue.y = y);
};
Object.defineProperty(this, "x", {
get: function getX () {
return stateValue.x;
},
enumerable: true
});
Object.defineProperty(this, "y", {
get: function getY () {
return stateValue.y;
},
enumerable: true
});
Object.defineProperty(this, 'valueOf', {
value: function valueOf () {
return Object.assign({}, stateValue);
}
});
Object.defineProperty(this, 'toString', {
value: function toString () {
return JSON.stringify(stateValue);
}
});
}
setPosition(x, y) { // prototypal method.
this.setX(x);
this.setY(y);
return this.valueOf();
}
}
class SameXYType extends BaseXYType {
// - Traits in JavaScript should be applicable types/objects that are
// just containers of trait and object composition rules which
// exclusively will be executed at a trait's apply time.
constructor(stateValue) { // injected state object.
super(stateValue);
withSameInternalXAndYState.call(this, stateValue);
}
}
var withSameInternalXAndYState = Trait.create(function (use, applicator) {
// local functions in order to enable shared code, thus achieving less memory consumption.
//
function afterReturningStateChangeXHandler(returnValue, argsArray, payloadList) {
var
stateValue = payloadList[0];
stateValue.y = argsArray[0]; // same y from x.
}
function afterReturningStateChangeYHandler(returnValue, argsArray, payloadList) {
var
stateValue = payloadList[0];
stateValue.x = argsArray[0]; // same x from y.
}
function setPositionInterceptor(proceedSetPosition, interceptor, argsArray, payloadList) {
var
x = argsArray[0],
y = argsArray[1];
if (x !== y) {
throw (new TypeError([x, "!==", y].join(" ")));
}
return proceedSetPosition.call(this, x, y);
}
applicator(function sameXAndYBehavior (stateValue) {
// no additional trait specific behavior within the applicator
}).requires([
"setX",
"setY",
"setPosition"
]).afterReturning(
"setX", afterReturningStateChangeXHandler
).afterReturning(
"setY", afterReturningStateChangeYHandler
).around(
"setPosition", setPositionInterceptor
);
});
var
base_1 = new BaseXYType({ x: 7, y: 11 }),
base_2 = new BaseXYType({ x: 99, y: 1 }),
same_1 = new SameXYType({ x: 13, y: 5 }),
same_2 = new SameXYType({ x: 99, y: 1 });
console.log('("" + base_1) : ', ("" + base_1));
console.log('("" + base_2) : ', ("" + base_2));
console.log('("" + same_1) : ', ("" + same_1));
console.log('("" + same_2) : ', ("" + same_2));
console.log('base_1.valueOf() : ', base_1.valueOf());
console.log('base_2.valueOf() : ', base_2.valueOf());
console.log('same_1.valueOf() : ', same_1.valueOf());
console.log('same_2.valueOf() : ', same_2.valueOf());
console.log('base_1.x : ', base_1.x);
console.log('(base_1.x = "foo") : ', (base_1.x = "foo"));
console.log('base_1.x : ', base_1.x);
console.log('base_1.y : ', base_1.y);
console.log('(base_1.y = "bar") : ', (base_1.y = "bar"));
console.log('base_1.y : ', base_1.y);
console.log('same_2.x : ', same_2.x);
console.log('(same_2.x = "biz") : ', (same_2.x = "biz"));
console.log('same_2.x : ', same_2.x);
console.log('same_2.y : ', same_2.y);
console.log('(same_2.y = "baz") : ', (same_2.y = "baz"));
console.log('same_2.y : ', same_2.y);
console.log('base_1.setY("foo") : ', base_1.setY("foo"));
console.log('base_1.y : ', base_1.y);
console.log('base_2.setX("bar") : ', base_2.setX("bar"));
console.log('base_2.x : ', base_2.x);
console.log('base_1.setPosition("brown", "fox") : ', base_1.setPosition("brown", "fox"));
console.log('("" + base_1) : ', ("" + base_1));
console.log('base_2.setPosition("lazy", "dog") : ', base_2.setPosition("lazy", "dog"));
console.log('("" + base_2) : ', ("" + base_2));
console.log('same_1.setY(543) : ', same_1.setY(543));
console.log('same_1.x : ', same_1.x);
console.log('same_1.y : ', same_1.y);
console.log('same_1.valueOf() : ', same_1.valueOf());
console.log('same_2.setY(79) : ', same_2.setY(79));
console.log('same_2.x : ', same_2.x);
console.log('same_2.y : ', same_2.y);
console.log('same_2.valueOf() : ', same_2.valueOf());
console.log('same_1.setPosition(77, 77) : ', same_1.setPosition(77, 77));
console.log('("" + same_1) : ', ("" + same_1));
console.log('same_2.setPosition(42, 42") : ', same_2.setPosition(42, 42));
console.log('("" + same_2) : ', ("" + same_2));
console.log('same_1.setPosition("apple", "pear") : ', same_1.setPosition("apple", "pear"));
console.log('("" + same_1) : ', ("" + same_1));
console.log('same_1.setPosition("apple", "pear") : ', same_1.setPosition("prune", "prune"));
console.log('("" + same_1) : ', ("" + same_1));
.as-console-wrapper { max-height: 100%!important; top: 0; }