Вопрос о том должен ли подкласс
«знать» структуру своего суперкласса довольно
интересен и требует отдельного рассмотрения.
Существует несколько решений, отражающих
всё многообразие ответов, от однозначного
«да», до столь же однозначного «нет».
Например, сторонники того, чтобы подкласс
«знал» структуру своего суперкласса, говорят,
что незнание структуры своего суперкласса
равносильно незнанию подклассом своей
собственной структуры. И это абсолютно
справедливо.
Оппоненты, в ответ на это,
возражают, заявляя, что в этом случае
нет, и не может быть инкапсуляции, то
есть сокрытия реализации. Любой разработчик
подкласса может обратиться к свойствам
суперкласса, минуя интерфейс, но при этом
не гарантируется правильность такого обращения.
Например, суперкласс имел некоторое поле,
которое должно принимать ограниченный
набор значений, и это требование неукоснительно
соблюдалось в суперклассе. Но разработчики
подкласса могли не знать об этом требовании
и, следовательно, могли выполнить присвоение
значения вне заданного диапазона, минуя
интерфейс. Теперь поведение подкласса
может стать неопределённым, если хотя
бы одно из свойств суперкласса, обращающееся
к этому полю, осталось не перекрытым.
Таким образом, инкапсуляция суперклассов
должна соблюдаться и для подклассов и
обращение к свойствам, наследованным от
суперклассов, должно производиться только
через объявленные интерфейсы.
Другим важным элементом
связи с суперклассами является наличие
конструкторов, которые работают при создании
объектов. В рамках конструктора подкласса
происходит каскадный вызов конструкторов
всех суперклассов. Это достаточно бессмысленная
и протяжённая операция.
Более простой и эффективный
способ инициализации объектов состоит
в инициализации по шаблону. Данный способ
не требует ни каскадных вызовов, ни знания
структуры создаваемого объекта. Всё, что
требуется от разработчика класса, это
указать значения, которые будут присвоены
полям при создании нового объекта. На
основании этих значений формируется шаблон
инициализации для конкретного класса.
Если этот класс имеет суперкласс, то полный
шаблон данного класса будет состоять из
шаблонов суперкласса, плюс собственный
шаблон. Шаблон хранится в системе вместе
с описанием класса. При создании объекта,
на выделенную под объект область памяти
накладывается шаблон данного класса, после
чего идентификатор нового объекта возвращается
его владельцу. Шаблон не нарушает инкапсуляцию,
поскольку не несёт в себе информации ни
о количестве, ни о типе полей какого-то
класса. Шаблон представляет собой просто
битовую маску.
Связи между сущностями характеризуют
сложность любой системы. Поэтому локализация
связей и ограничение их видов способствуют
понижению сложности системы. Отсюда проистекает
рекомендация локализации связей элементарного
класса. Элементарные классы должны поддерживать
связи только между своими свойствами,
но сами не должны иметь непосредственных
связей с другими классами. Это обеспечивает
простоту и более высокий уровень повторного
использования класса. С другой стороны,
независимость элементарных объектов позволяет
проще организовывать их параллельную работу.
Взаимодействие между элементарными
классами осуществляется посредством контейнера.
Контейнер на основе определения роли каждого
вложенного объекта определяет связи между
ними при обработке внешних запросов. Эти
связи выражаются в виде схем (графов),
где можно легко определить этапы последовательного
и параллельного исполнения ветвей. Вынесение
связей между вложенными объектами на уровень
контейнера имеет своей целью отделения
логики работы элементарного класса, от
логики управления этим классом. Связи,
представленные в виде схем, образуют логику
контейнера, существо которой состоит в
управлении вложенными объектами для выполнения
той или иной совместной работы (цели).
Разделение логики исполнения от логики
управления принципиально важно. И вынесение
связей между вложенными объектами на отдельный
более высокий уровень, является основой
образования логики управления.
Аналогично тому, как локализация
связей между свойствами является основой
построения элементарных классов, точно
также локализация связей между объектами
внутри контейнера является основой для
построения контейнеров. Контейнер является
не только агрегатом вложенных объектов,
но и проводником необходимого сервиса
для этих объектов. Таким образом, любой
контейнер образует локализованную среду
исполнения для вложенных в него классов.
Тот факт, что реальное предоставление
того или иного сервиса может осуществляться
не контейнером, а внешним программным
обеспечением, остаётся скрытым от разработчиков
элементарных классов.
Физическое представление
связей между контейнером и вложенными
в него объектами тоже должно быть прозрачным
как для разработчиков конкретных контейнеров,
так и для разработчиков элементарных классов.
Конкретное представление связи может быть
сформировано только в момент создания
(загрузки в память) контейнера для исполнения.
Как следствие, увеличивается гибкость
использования вложенных объектов. Например,
для одного и того же контейнера в одном
сеансе вложенные объекты могут располагаться
в одном адресном пространстве, а в другом
сеансе они могут быть разнесены по нескольким
адресным пространствам. Это положение
станет актуально, когда будет понято преимущество
(необходимость) использования перемещаемых
(кочующих) ресурсов.
Основной формой связи должны
быть асинхронные вызовы, поскольку с одной
стороны, они при необходимости могут быть
сведены к синхронным вызовам, а с другой
стороны, данная форма связи обеспечивает
максимальную гибкость, производительность
и защищённость системы в целом. В тех
случаях, когда требуется последовательная
работа объектов в одном адресном пространстве,
система может самостоятельно привести
асинхронные вызова к синхронным в момент
загрузки той или иной схемы контейнера.
Локализация, как элементарных
объектов, так и контейнеров, а также связь
на основе сообщений позволят строить параллельные
(в том числе и распределённые) системы
столь же просто и естественно, как и обычные
системы, не обладающие параллелизмом.
Мало того, уровень параллелизма может
изменяться непосредственно при работе
системы, в случае, например, если становятся
доступными новые вычислительные ресурсы.
В свою очередь, переход к системам с массовым
параллелизмом позволит не только наращивать
производительность пропорционально увеличению
количества процессоров, но использовать
иные подходы при решении сложных задач.
|