Tuesday, 11 September 2018

Mixin and Compose

While thinking of a possible design I came up with an idea that seemed interesting to me. I was thiking of having a main class and organizing additional behaviours in objects to be used as mixins. We would create instances of the main class and add extra behaviours by combining them with different mixins as we saw fit. OK, nothing out of the ordinary, the interesting part is that some of the behaviours (methods) I would not just want to add them, but to compose them (create a new method that calls to the other methods).

What I mean is that if I have a class with a method "process", and I mixin with one of its instances some object that also has a "process" method (we have a collision then), we would end up with a new "process" method that invokes both methods. Probably this is not the generic behaviour that we want for our mixins and collisions, in other occasions we would want the last "process" method to be added to override the previous ones, or maybe just throw an error, but for some situations it's a behaviour that makes quite sense.

So I've cooked a mixinAndCompose basic implementation and sample:

class Particle{
	constructor(position, velocity){
		console.debug("Particle constructor");
	}

	evolve(){
		console.debug("Particle updates position based on velocity");
	}
}

//animation logic to be "mixed in"
const alphaAnimateLogic = {
	initAlphaAnimate(minAlpha, maxAlpha, currentAlpha, initialDirection){
		this.minAlpha = minAlpha;
		this.maxAlpha = maxAlpha;
		this.currentAlpha = currentAlpha;
		this.initialDirection = initialDirection;
	},
	
	additionalMethod(){
		console.debug("additionalMethod: " + this.currentAlpha);
	},


	evolve(){
		this.currentAlpha++;	
		console.debug("Particle updates alpha values");		
		this.additionalMethod();	
		
	}
}

//bouncing logic to be "mixed in"
const bounceLogic = {
	initBounce(xLimit, yLimit){
		//...
	},
	
	

	evolve(){
		console.debug("applying bounce logic");
	}
};

//---------------------------

function mixinAndCompose(target, mixinObj){
	for (let propName of Object.getOwnPropertyNames(mixinObj)){
	 	if (typeof(mixinObj[propName]) == "function" && typeof(target[propName]) == "function"){
			console.log("composing: " + propName);			
			let targetFn = target[propName];
			let mixinFn = mixinObj[propName];
			target[propName] = function(...args){
				targetFn.apply(this, args);
				return mixinFn.apply(this, args);

			};
		}
		else{
			console.log("adding: " + propName);				
			target[propName] = mixinObj[propName];
		}
	}
	return target;
}


let particle = mixinAndCompose(new Particle(), alphaAnimateLogic);
mixinAndCompose(particle, bounceLogic);
particle.initAlphaAnimate(0,10,0);
particle.initBounce();
console.log("--------");

particle.evolve();
console.log("--------");

particle.evolve();
console.log("--------");

particle.evolve();

This strategy to deal with mixins in collisions could also make sense in some occasions for collisions in Multiple Inheritance

No comments:

Post a Comment