I have a Genome class that contains an array of bits ("genes"), and I would like to implement different mutation methods to change the genes. I'm currently implementing the mutation method using a Strategy Pattern, but the mutation method changes the Genome class directly.
Is this consider breaking encapsulation if I pass in the genes array into the Strategy and it modifies the array directly (naiveMutationStrategy)? I was reading some arguments that the Strategy pattern should not modify the state of the context calling it?
What if I directly passed in the Genome class (naiveMutationStrategy2) - I assume this is a no-no since the strategy pattern shouldn't store a reference to the context object?
function Genome(genes, mutationStrategy) {
this.genes = genes; //an array
this.mutationStrategy = mutationStategy;
}
Genome.prototype.mutate = function() {
this.mutationStategy.execute(this.genes);
}
function naiveMutationStrategy() {};
//converts '1' into '0' and vice versa
mStrategy.prototype.execute = function(genes) {
_.each(genes, function(element) {
if (element === '0') {
element = '1'
} else element = '0';
});
};
function naiveMutationStrategy2() {};
//converts '1' into '0' and vice versa
naiveMutationStrategy2.prototype.execute = function(genome) {
_.each(genome.genes, function(element) {
if (element === '0') {
element = '1'
} else element = '0';
});
};
I'm currently implementing the mutation method using a Strategy Pattern
Good idea, but don't try to reproduce the pattern you've learned for a class-based language too closely. Your constructor functions for the strategies are empty - they're unnecessary (unless you want some kind of "StrategyFactory"). Even those prototype objects with their methods are unncessary, they have only a single method.
JavaScript is a functional language, with first-class function objects. In a functional language, a "strategy" is just a function. Pass functions, not Strategy
instances.
but the mutation method changes the Genome class directly.
Not really - it does not assign to the properties of Genome
instances. All it does is to mutate the arguments that it got passed, which is (more or less) fine.
Is this consider breaking encapsulation if I pass in the genes array into the Strategy and it modifies the array directly (naiveMutationStrategy)?
No. The contracts are "the strategy expects an array argument" and "the strategy might modify its argument" - if you don't like the latter than use a different one. Really breaking encapsulation would be if the strategy got passed the Genome
instance and would modify its properties from outside. As long your mutate
method controls what is going on, I can't see a problem.
I was reading some arguments that the Strategy pattern should not modify the state of the context calling it?
Yes, it certainly is a good idea. If the genes
were immutable and the strategy did create a new sequence of genes, your code would benefit.
_.each(genes, function(element) { if (element === '0') { element = '1' } else element = '0'; });
Notice that you're actually not mutating anything here. element
is a local variable, not a reference to the array element. If you'd really wanted to mutate the array, you'd use
for (var i=0; i<genes.length; i++)
if (genes[i] === '0')
genes[i] = '1'
else
genes[i] = '0';
and if you wanted to create a new sequence, you'd use
return _.map(genes, function(element) {
return (element === '0') ? '1' : '0';
});