Let's assume we have a few loosely coupled components that form a pipeline. At the end is an file generating component. A new requirement comes in that requires an parallel alternative behaviour for the last component. The interface doesn't change, only the internal behaviour. Great! This is what OOP and Dependency injection is all about. I create a seperate implementation, and then it's just about wiring it all up. But, here's the issue: the new behaviour is decided by user input and thus is aaaall the way at the "start" of the pipeline. I can't think of a clean way on how to wire things up. Here's the approaches I thought of and why I dislike them:
1 - Pass the argument through the entire pipeline and then decide on the new behaviour in the second to last component. Obviously the worst choice, since I augment every single interface. Yikes.
2 - Save the user input in some form of global (request-scoped) variable and retrieve it in the module that would call the possible alternative implementation. Pretty bad option since globals are usually a no-no and I modify a class that doesn't even really have anything to do with the behaviour
3 - Do what's OOP actually good at and simply inject the different behaviour at wire-up time. But for that I need to have a second calling object that has the different injected object... and inject that new object in another second object that's one level higher up in the pipeline... all the way up to the top, where I then can easily start the "chosen" pipeline based on the user input. While this would be imho the "clean" solution of not having to modify any code except the entry point and the new injected class, I'd have to create a lot of Beans (Spring/Java here) that are in the end nothing more than a duplicate version of the already existing pipeline object, basically creating a parallel pipeline that's completely identical except the ending. And that seems incredibly dirty and unflexible.
So, what did I miss? There must be some better or established way to deal with this. Perhaps inject different behaviour at runtime, but that could still requires me to pass through some strategy through modules that got nothing to do with this.