I currently have an immutable type called Gene, that only has 2 fields:
double value;
Interval intervalOfAllowedValues;
I’ll need sometimes to have a Gene switch randomly its value to some other value as long as it is still in the range defined in intervalOfAllowedValues.
I had made for this, a special method
public Gene RandomMutation() { ... }
//it returns a Gene because this class is immutable!
that’d take care of the situation, using a INumberGenerator.GenerateDouble(...). The problem is that either RandomMutation() accepts a IRandomNumberGenerator as argument or then Gene will have to take one by constructor injection.
Neither of the solutions are of my liking:
If RandomMutation() accepts the number generator by argument, it means that now not only does Gene have to know about INumberGenenerator but also the class that contains it.
If on the other hand I pass by constructor injection an INumberGenerator to this Gene, most of the time I’ll have it there without making any use of it, which doesn’t seem nice. I feel it kinda defeats the purpose of having this Gene object exist as an value type.
It also raises the subtle question of whether 2 different Genes are equal if they have different number generators. If yes, how to Unit-Test it?
There is a third option: I take RandomMutation() away from the Gene class. The problem now is that the class that contains a Gene will have to know both about Gene and about Interval, which might not be desirable. Also, behaviour should be near its data, and that wouldn’t be the case when following this approach.
There is yet a 4th(!) approach: making the number generator global (Singleton). That’d work wonders, but it’d go against the philosophy of making every dependency explicit.
How’d you handle this situation?
Thanks
It is not a useful metric to evaluate how much a dependency like
INumberGeneratoris utilized throughout the lifetime of a particularGeneinstance. It is only useful to see how many times it is used by the definition ofGene. If it is used even once, it deserves to be treated like a first-class dependency.The bigger issue is in having a
structwith dependencies at all.RandomMutationmust have anINumberGeneratorto function, but no matter whether you use constructor or property injection, anyone can callnew Gene()and create an instance with null dependencies. There is simply no way for you to enforce that dependencies have been supplied to astruct.You have a couple of options. The first is to define
Geneas aclass, which will let you declareINumberGeneratoras a required dependency through a constructor argument. This is the object-oriented answer.Your second option is use a more functional approach, which leaves
Geneas astructand defines the functionality externally. Functional programs tend to separate data structures from the logic which acts upon them (see LINQ), as opposed to OO which strives to combine behavior and data. Your problem sounds like it would best benefit from being modeled in this fashion.The core issue still remains, though, which is that you need both a
Geneand anINumberGeneratorin order to mutate randomly. Since we have determined thatGeneprobably shouldn’t know aboutINumberGeneratordirectly, you can model it as an extension method:This takes the
INumberGeneratordependency out ofGeneand bubbles it up to the object which knows when genes should be mutated.