I've been building an application that processes documents through different filters.
I have a class (or better as an interface for creating test-doubles?) called Document
. The Document
class contains many fields and therefore I was thinking about using the Builder pattern for construction.
Now, there are certain "specialized" documents that contain additional fields (let's call it SpecialDocument
). The obvious solution would be to extend the Document
class. However, how would I then (during runtime) take a Document
instance and make it a SpecialDocument
instance? Remember that the construction of the Document
instance is quite complex, therefore I think, a copy-constructor is too tedious and error-prone (when adding a filed etc.).
Another solution that I can think of is to pass the original Document
instance to the SpecialDocument
constructor and then delegate all document calls to that instance.
Or should I maybe just add those fields from SpecialDocument
to the Document
class and then provide optional getters, like so Optional<String> getSpecialField
? This last option seems to be the least preferable since it mixes concepts and the client would never be sure about the type of document it is dealing with (besides having to deal with the optional first).
Is there a simpler and more elegant way to model this scenario?
Thank you very much for you advice.
-
1Try en.wikipedia.org/wiki/Composition_over_inheritance <- this approach.Thomas Junk– Thomas Junk12/29/2019 13:52:01Commented Dec 29, 2019 at 13:52
-
As @ThomasJunk points out in his comment, Inheritance is not very flexible. Also, I find it's more helpful to focus on real-world, concrete examples. When you try to design something that's too generalized, it leads to speculative generality, which is a design smell.Fuhrmanator– Fuhrmanator12/29/2019 16:35:51Commented Dec 29, 2019 at 16:35
2 Answers 2
The class of an object is not supposed to change during the object life-cycle. If you think it should, there must be something wrong in the design.
The kind of challenge that you describe is usually addressed with:
- the decorator pattern: a decorator
SpecialDocument
would add some responsibilities to the base classDocument
, such as some more methods, or some more properties, or both together. - the entity component system: an entity
Document
would have some components such asSpecialDocument
, which would define a set of properties and behaviors. - a higher level of abstraction:
Document
would be defined in such a way that a strategy could e set dynalically to alter its behavior.
The common denominator behind these design patterns is the creative use of composition that is preferred over inheritance whenever the thought of changin class dynamically crosses your mind.
Most often I'd do this:
class SpecialDocument {
Document getDocument();
String getField1();
String getField2();
}
The SpecialDocument
doesn't extend Document
, instead it contains a reference to a Document which is returned upon request.
- This avoids creating a complex copy constructor
- This avoids creating a bunch of delegation methods
- This allows a function to explicitly take either a
Document
orSpecialDocument
depending on what the function needs.
Explore related questions
See similar questions with these tags.