Simple Shell

Общая диаграмма: Class diagram

Начнем с главного класса, Shell

Его мы создаем, с указанием нужных нам параметров (можно было сделать через builder, но и так слишком много классов уже) и запускаем через start(). В нем у нас хранятся основные объекты для парсинга и исполнения команд.

В процессе парсинга мы получаем ShellScript – по сути список TaskDescription, для команд, объединенных через pipe. А он, в свою очередь, хранит название команды и список ее аргументов.

После того как парсер отдал нам ShellScript, мы в нем заменяем переменные с помощью Environment.

Далее, мы вызываем сохраненный в Shell'e ShellScriptRunner (интерфейс как всегда для гибкости, пока что реализует его только один класс)

Сам runner должен с помощью TaskFactory из TaskDescription сделать непосредственно различные таски

При этом есть специальный класс AutoTaskFactory, которых хранит список фабрик, и пробует поочередно создавать с их помощью таски, а именно вызывает у них метод tryCreate, который возвращает либо null, либо не null.

Далее, эти таски отдаются в CommandRunner, и у этого интерфейса есть 2 реализации: SimpleCommandRunner и MultiThreadCommandRunner.

Разница у них в том, что первый запускает команды разделенные pipe'ом поочередно, а второй запускает их в N потоков и они передают друг другу результаты выполенения через PipedInputStream и PipedOutputStream. Это позволяет делать такие вещи как: cat /dev/urandom | echo (Пример бесполезный, но если расширять список команд до filter и takeFirst, то будет осмысленней) Не слишком премудрый объект Command: