Основы объектно-ориентированного проектирования


Программируемые процессы


Поскольку мы готовы избавиться от активных объектов, полезно заметить, что на самом деле мы не хотим ни от чего отказываться. Объект способен выполнять много операций: все компоненты породившего его класса. Превращая объект в процесс, приходится выбирать одну из этих операций в качестве единственной реально вычисляемой. Это не дает абсолютно никаких преимуществ! Зачем ограничивать себя одним алгоритмом, когда можно иметь их столько, сколько нужно?

Заметим, что понятие процесса не обязательно должно быть встроено внутрь механизма параллельности; процессы можно программировать, рассматривая их как обычные программы. Процесс для принтера, приведенный в начале лекции, с ОО-точки зрения может трактоваться как одна из подпрограмм, скажем, live, соответствующего класса:

indexing description: "Принтер, выполняющий в каждый момент одно задание" note: "Улучшеная версия, основанная на общем классе PROCESS, % %появится далее под именем PRINTER" class PRINTER_1 feature -- Status report stop_requested: BOOLEAN is do ... end oldest: JOB is do ... end feature -- Basic operations setup is do ... end wait_for_job is do ... end remove_oldest is do ... end print (j: JOB) is do ... end feature -- Process behavior live is -- Выполнение работы принтера do from setup until stop_requested loop wait_for_job; print (oldest); remove_oldest end end ... Другие компоненты ... end

Отметим заготовку для других компонентов: хотя до сих пор все наше внимание было уделено live и окружающим его компонентам, мы можем снабдить процесс и многими другими желательными компонентами, чему способствует ОО-подход, развитый в других частях этого курса. Превращение объектов класса PRINTER_1 в процессы означало бы ограничение этой свободы, это была бы существенная потеря в выразительной силе без всякой видимой компенсации.

Абстрагируясь от этого примера, который описывает конкретный тип процесса просто как некоторый класс, можем попытаться предложить более общее описание всех типов процессов с помощью специального отложенного класса - класса поведения, как это уже не раз делалось в предыдущих лекциях.
Процедура live будет применима ко всем процессам. Мы можем оставить ее отложенной, но нетрудно заметить, что большинство процессов будут нуждаться в некоторой инициализации, некотором завершении, а между ними - в некотором основном шаге, повторяемом некоторое число раз. Поэтому мы можем учесть это на самом абстрактном уровне:

indexing description: "Самое общее понятие процесса" deferred class PROCESS feature -- Status report over: BOOLEAN is -- Нужно ли сейчас прекратить выполнение? deferred end feature -- Basic operatios setup is -- Подготовка к выполнению операций процесса -- (по умолчанию: ничего) do end step is -- Выполнение основных операций deferred end wrapup is -- Выполнение операций завершения процесса -- (по умолчанию: ничего) do end feature -- Process behavior live is -- Выполнение жизненного цикла процесса do from setup until over loop step end wrapup end end

Методологическое замечание: компонент step является отложенным, но setup и wrapup являются эффективными процедурами, которые по определению ничего не делают. Так можно заставить каждого эффективного потомка обеспечить собственную реализацию основного действия процесса step, не беспокоясь об инициализации и завершении, если на этих этапах не требуется специальных действий. При проектировании отложенных классов выбор между отложенной версией и пустой эффективной версией приходится делать регулярно. Ошибки не страшны, поскольку в худшем случае потребуется выполнить больше работы по эффективизации или переопределению у потомков.
Используя данный образец, можно определить специальный класс, охватывающий принтеры:

indexing description: "Принтеры, выполняющие в каждый момент одно задание" note: "Пересмотренная версия, основанная на классе PROCESS" class PRINTER inherit PROCESS rename over as stop_requested end feature -- Status report stop_requested: BOOLEAN -- Является ли следующее задание в очереди запросом на -- завершение работы? oldest: JOB is -- Первое задание в очереди do ...


end feature -- Basic operations step is -- Обработка одного задания do wait_for_job; print (oldest); remove_oldest end wait_for_job is -- Ждать появления заданий в очереди do ... ensure oldest /= Void end remove_oldest is -- Удалить первое задание из очереди require oldest /= Void do if oldest.is_stop_request then stop_requested := True end "Удалить первое задание из очереди" end print (j: JOB) is -- Печатать j, если это не запрос на остановку require j /= Void do if not j.is_stop_request then "Печатать текст, связанный с j" end end endЭтот класс предполагает, что запрос на остановку принтера посылается как специальное задание на печать j, для которого выполнено условие jlis_stop_request. (Было бы лучше устранить проверку условия в print и remove_oldest, введя специальный вид задания - "запрос на остановку"; это нетрудно сделать [см. У12.1]).

Уже сейчас видны преимущества ОО-подхода. Точно так же, как переход от главной программы к классам расширил наши возможности, предоставив абстрактные объекты, не ограничивающиеся "только одним делом", рассмотрение процесса принтера как объекта, описанного некоторым классом, открывает возможность новых полезных свойств. В случае принтера можно сделать больше, чем просто выполнять обычную операцию печати, обеспечиваемую live (которую нам, возможно, придется переименовать в operate, при наследовании ее из PROCESS).

Можно добавить компоненты: perform_internal_test (выполнить внутренний тест), switch_to_Postscript_level_1(переключиться на уровень Postscript1) или set_resolution (установить разрешение). Стабилизирующее влияние ОО-метода здесь так же важно, как и для последовательного ПО.

Классы, очерченные в этом разделе, показывают, как можно применять нормальные ОО-механизмы - классы, наследование, отложенные элементы, частично реализованные образцы - к реализации процессов. Нет ничего плохого в понятии процесса в контексте ОО-программирования, и на самом деле оно требуется во многих параллельных приложениях.Но, вместо включения некоторого примитивного механизма, оно просто охватывается библиотечным классом PROCESS, основанным на версии, приведенной выше в этом разделе или, может быть, несколькими такими классами, охватывающими различные варианты этого понятия.

Что касается новой конструкции для параллельной ОО-технологии, то она будет рассмотрена далее.


Содержание раздела