Selecting Object Implementation at Runtime
Learn which design patterns to use when object implementation is unknown.
Problem statement
Let’s imagine that we have a code that uses a particular object type. The code that uses this object only needs to know the signatures of accessible methods and properties of the object. It doesn’t care about the implementation details. Therefore, using an interface that represents the object rather than a concrete implementation of the actual object is desirable.
If we know what concrete implementation our application will use ahead of time, we can apply the dependency inversion principle and some simple dependency injection. In this case, we’ll know that it’s always a particular interface implementation used throughout our working code, and we’ll only replace it with mocked objects for unit testing. However, what if the concrete implementation of the interface needs to be chosen dynamically based on runtime conditions? Or what if the object’s shape needs to be defined based on input parameters in the actual place where the object is about to be used? In either of these cases, the dependency injection approach wouldn’t work.
Let’s look at an example of this. Imagine that we’re building an application that can play audio. The application should be able to work on both Windows and Linux. Inside it, we have an interface called IPlayer
that has standard methods that an audio player would have, such as Play
, Pause
, and Stop
. It’s the implementation of this interface that actually interacts with the operating system and plays the audio.
The problem is that Windows and Linux have entirely different audio architectures; therefore, we can’t just have a single concrete implementation of the IPlayer
interface that would work on both operating systems. We won’t know ahead of time what operating system our application will run on.