Let us assume you don't want to turn SomethingData
into a mutable class (of course, a blurry name like SomethingData
isn't particular helpful to evaluate such design decisions, but let us assume this makes most sense for this case).
Without introducing a DI container, your solution #1 is the straightforward solution to this I would recommend. The basicInfo
object can now be initialized step-by-step ("through a series of transformations"), and any duplicate code in this initializations can be refactored out into separate functions (or, as the question demonstrates it, into some basicInfoFactory
class).
but there would still be at the very least N duplicated calls for N concrete sub-classes.
Yes, but these calls are just delegation calls, they don't duplicate any real logic, hence they are not a DRY violation. They have actually the opposing purpose of reusing non-repeated logic.
When you have code which deals with N different subclasses, you will need at least N different constructor calls. If those subclasses are immutable, there will be some individual initialization logic required for each of those subclasses which cannot be put into member functions of that class, since all the initialization must be completed before an object is constructed.
There is indeed another solution, as I mentioned at the beginning: making use of a DI container. For this, you would introduce an interface IBasicInfo
into your constructor parameters, safe the factory calls, and let the container figure out the necessary factory calls and constructor calls.