This allows task initialization to be moved out of ShellDispatcher where it does not belong, as tasks are similar to components. Updating parts of TaskCollection, as the Dispatcher is still required to be passed around.
Making TaskCollection pass the stdout, stdin, stderr to Tasks they create. This allows for more flexible dependency injection and makes testing easier.
This will help reduce the coupling between ShellDispatcher and other objects.
Since ShellDispatcher never directly uses or interacts with TaskCollection, it doesn't make much sense for it to have one. Instead shells will either get their own, or be passed one in.