Assuming we have polymorphic entities such as the following, with constructors enforcing invariants (assume there could be lots of sub-classes). What would be an effective/elegant approach to construct each concrete instance type while avoiding excessive code duplication?
abstract class Something {
Data fieldA;
Data fieldB;
Something(Data fieldA, Data fieldB) {
...
}
}
class SomethingA extends Something {
Data fieldC;
Data fieldD;
Something(Data fieldA, Data fieldB, Data fieldC, Data fieldD) {
...
}
}
class SomethingB extends Something {
Data fieldE;
Data fieldF;
Something(Data fieldA, Data fieldB, Data fieldE, Data fieldF) {
...
}
}
For instance, assume fieldA
and fieldB
are complex to resolve, we'd want to avoid:
createSomethingA(dto) {
fieldA = fieldAResolver.resolveFor(dto); //this will get duplicated everywhere
fieldB = fieldBResolver.resolveFor(dto);
return new SomethingA(fieldA, fieldB, dto.fieldC, dto.fieldD); //mapping logic as well
}
createSomethingB(dto) {
fieldA = fieldAResolver.resolveFor(dto);
fieldB = fieldBResolver.resolveFor(dto);
return new SomethingB(fieldA, fieldB, dto.fieldE, dto.fieldF);
}
When entities don't have to be constructed in a valid state, it's easy as you can just start with an empty entity, and then apply a series of transformations. But that can't be done for always-valid entities. The only approaches I could think of right now are:
Group fine-grained data bits into more coarse-grained bits. For instance, we could group
fieldA
andfieldB
into aBasicInfo
value object, and then do something likebasicInfo = basicInfoFactory.from(dto)
, reducing the number of duplicate calls, but there would still be at the very leastN
duplicated calls forN
concrete sub-classes.Change the whole constructor signature to take a single
SomethingData
class instance which is mutable. This allows us to constructSomethingData
through a series of transformations instead. In the end, theSomething
classes would have null checks to perform, orOptional
checks to ensure the data they need is present or else throw. (Same as #1 really, but with mutability).
Are there other alternatives or patterns for that?
EDIT:
Just to give a bit more context, it's a request management system with disparate request types. The requests have a bit of common info and workflow behaviors, but have very distinctive shapes and rules overall. Some of the rules may span the entire request's data. It's a CRUD-based system for the most part or at least this seemed to be the simplest route.
Perhaps inheritance is a mistake. Another drastically different approach could have been to use a schema-driven design, where we shape requests through linked data components. Components would just be associated (no composition) and most likeky changed together in a single tx for consistency.
Customer extends Person
is a possible situation in theory, but in practice, it will lead to undesired coupling.